Added Network status checking to fix Bug [#6294] Becoming disconnected from network...
[vlc-remote] / src / playermainwindow.cpp
1   /*   VLC-REMOTE for MAEMO 5
2   *   Copyright (C) 2010 Schutz Sacha <istdasklar@gmail.com>, Dru Moore <usr@dru-id.co.uk>, Yann Nave <yannux@onbebop.net>
3   *   This program is free software; you can redistribute it and/or modify
4   *   it under the terms of the GNU General Public License version 2,
5   *   or (at your option) any later version, as published by the Free
6   *   Software Foundation
7   *
8   *   This program is distributed in the hope that it will be useful,
9   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   *   GNU General Public License for more details
12   *
13   *   You should have received a copy of the GNU General Public
14   *   License along with this program; if not, write to the
15   *   Free Software Foundation, Inc.,
16   *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17   */
18   #include <QDebug>
19   #include <QTime>
20   #include <QtGui>
21   #include "playermainwindow.h"
22   #include "ui_playermainwindow.h"
23   #include "configdialog.h"
24   #include "aboutdialog.h"
25   #include "accountdialog.h"
26   #include "appsettings.h"
27 #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
28 #include <QMaemo5InformationBox>
29 #endif
30
31   PlayerMainWindow::PlayerMainWindow(QWidget *parent) :
32           QMainWindow(parent),
33           ui(new Ui::PlayerMainWindow)
34   {
35       ui->setupUi(this);
36       setWindowTitle("Vlc remote");
37
38
39
40       mTimer = new QTimer(this);
41       mNetManager = new QNetworkAccessManager(this);
42       mPlayListMainWindow = new PlayListMainWindow;
43       mBrowserMainWindow = new BrowseMainWindow;
44       mFavouritesMainWindow = new FavouritesMainWindow;
45
46       mVolume = 100;
47       mMuted = false;
48
49       mIsLandscape = true;
50
51       ui->playlistButton->setIcon(QIcon::fromTheme("notes_bullets"));
52       ui->browseButton->setIcon(QIcon::fromTheme("filemanager_media_folder"));
53
54       ui->previousButton->setIcon(QIcon::fromTheme("pdf_viewer_first_page"));
55       ui->nextButton->setIcon(QIcon::fromTheme("pdf_viewer_last_page"));
56       ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
57       ui->stopButton->setIcon(QIcon::fromTheme("camera_video_stop"));
58       ui->fullscreenButton->setIcon(QIcon::fromTheme("general_fullsize"));
59       ui->volDown->setIcon(QIcon::fromTheme("statusarea_volumelevel1"));
60       ui->volUp->setIcon(QIcon::fromTheme("statusarea_volumelevel4"));
61       ui->volMute->setIcon(QIcon::fromTheme("statusarea_volume_mute"));
62
63       ui->labelArtPortrait->setVisible(false);
64       ui->labelArtLandscape->setVisible(false);
65
66       ui->labelTitle->setTextFormat(Qt::RichText);
67       ui->labelArtist->setTextFormat(Qt::RichText);
68       ui->labelAlbum->setTextFormat(Qt::RichText);
69
70
71   #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
72
73       mPlayListMainWindow->setParent(this);
74       mPlayListMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow, true);
75       setAttribute(Qt::WA_Maemo5StackedWindow, true);
76       mPlayListMainWindow->setWindowFlags(mPlayListMainWindow->windowFlags() | Qt::Window);
77
78       mBrowserMainWindow->setParent(this);
79       mBrowserMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow, true);
80       setAttribute(Qt::WA_Maemo5StackedWindow, true);
81       mBrowserMainWindow->setWindowFlags(mBrowserMainWindow->windowFlags() | Qt::Window);
82
83       mFavouritesMainWindow->setParent(this);
84       mFavouritesMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow, true);
85       setAttribute(Qt::WA_Maemo5StackedWindow, true);
86       mFavouritesMainWindow->setWindowFlags(mFavouritesMainWindow->windowFlags() | Qt::Window);
87
88       connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(orientationChanged()));
89
90   #endif
91
92       connect(mTimer,SIGNAL(timeout()),this,SLOT(askStatus()));
93       connect(ui->actionConfiguration,SIGNAL(triggered()),this,SLOT(showConfig()));
94       connect(ui->actionAbout,SIGNAL(triggered()),this,SLOT(showAbout()));
95       connect(ui->actionPortrait,SIGNAL(triggered()),this,SLOT(setPortrait()));
96       connect(ui->actionLandscape,SIGNAL(triggered()),this,SLOT(setLandscape()));
97       connect(ui->actionAutoRotate,SIGNAL(triggered()),this,SLOT(setAutoRotate()));
98       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(show()));
99       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(showPlayList()));
100       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(show()));
101       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(showCurrentDirectory()));
102
103       connect(ui->playpauseButton,SIGNAL(clicked()),this,SLOT(playpause()));
104       connect(ui->stopButton,SIGNAL(clicked()),this,SLOT(stop()));
105       connect(ui->previousButton,SIGNAL(clicked()),this,SLOT(previous()));
106       connect(ui->nextButton,SIGNAL(clicked()),this,SLOT(next()));
107       connect(ui->fullscreenButton,SIGNAL(clicked()),this,SLOT(fullscreen()));
108       connect(ui->volUp,SIGNAL(clicked()),this,SLOT(volUp()));
109       connect(ui->volDown,SIGNAL(clicked()),this,SLOT(volDown()));
110       connect(ui->volMute,SIGNAL(clicked()),this,SLOT(volMute()));
111       connect(ui->slider,SIGNAL(sliderMoved(int)),this,SLOT(slide(int)));
112
113       connect(mPlayListMainWindow, SIGNAL(idUpdated(int,bool,QString)), this, SLOT(playlistIdUpdated(int, bool, QString)));
114       connect(mBrowserMainWindow, SIGNAL(showFavouritesWindow()), this, SLOT(showFavourites()));
115
116       connect(mFavouritesMainWindow, SIGNAL(browseDirectory(QString)), mFavouritesMainWindow, SLOT(close()));
117       connect(mFavouritesMainWindow, SIGNAL(browseDirectory(QString)), mBrowserMainWindow, SLOT(browseDirectory(QString)));
118       connect(mFavouritesMainWindow, SIGNAL(closeSignal()), mBrowserMainWindow, SLOT(show()));
119
120
121       // check if last used connection is still valid or showConfig
122
123       // check for network
124       if (AppSettings::isConnected()) {
125           QSettings settings;
126           QString last_ip = AccountDialog::currentIp();
127           if (!last_ip.isNull() && !last_ip.isEmpty()) {
128               QTcpSocket * socket = new QTcpSocket;
129               if(last_ip.contains(":"))
130               {
131                   QStringList hostSplit = last_ip.split(":");
132                   QString ip   = hostSplit.at(0);
133                   QString port = hostSplit.at(1);
134                   socket->connectToHost(ip,port.toInt());
135               }
136               else {
137                   socket->connectToHost(last_ip,8080);
138               }
139               if (!socket->waitForConnected(1000)) {
140                      showConfig();
141                  }
142               else {
143                   mIp= last_ip;
144
145                  mPlayListMainWindow->init();
146                  mBrowserMainWindow->init();
147                  mTimer->start(5000);
148                  askStatus();
149               }
150               delete socket;
151           }
152           else {
153             showConfig();
154           }
155       }
156       else {
157 #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
158         QMaemo5InformationBox::information(this, tr("Network unavailable!"), QMaemo5InformationBox::DefaultTimeout);
159 #endif
160           showConfig();
161       }
162   }
163   
164
165   PlayerMainWindow::~PlayerMainWindow()
166   {
167       delete ui;
168   }
169
170   void PlayerMainWindow::changeEvent(QEvent *e)
171   {
172       QMainWindow::changeEvent(e);
173       switch (e->type()) {
174       case QEvent::LanguageChange:
175           ui->retranslateUi(this);
176           break;
177       default:
178           break;
179       }
180   }
181
182   void PlayerMainWindow::showFavourites() {
183       mFavouritesMainWindow->show();
184       mFavouritesMainWindow->init();
185   }
186
187   void PlayerMainWindow::setPortrait()
188   {
189     #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
190       AppSettings::setOrientation(PORTRAIT);
191     this->setAttribute(Qt::WA_Maemo5PortraitOrientation, true);
192     #endif
193   }
194
195   void PlayerMainWindow::setLandscape()
196   {
197     #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
198       AppSettings::setOrientation(LANDSCAPE);
199     this->setAttribute(Qt::WA_Maemo5LandscapeOrientation, true);
200     #endif
201   }
202
203   void PlayerMainWindow::setAutoRotate()
204   {
205     #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
206       AppSettings::setOrientation(AUTO_ROTATE);
207     this->setAttribute(Qt::WA_Maemo5AutoOrientation, true);
208     #endif
209   }
210
211   void PlayerMainWindow::orientationChanged() {
212       QRect screenGeometry = QApplication::desktop()->screenGeometry();
213       mIsLandscape = (screenGeometry.width() > screenGeometry.height());
214       if (mHasImage) {
215           if (mIsLandscape) {
216               ui->labelArtPortrait->setVisible(false);
217               ui->labelArtLandscape->setVisible(true);
218           }
219           else {
220               ui->labelArtLandscape->setVisible(false);
221               ui->labelArtPortrait->setVisible(true);
222           }
223       }
224       else {
225           ui->labelArtLandscape->setVisible(false);
226           ui->labelArtPortrait->setVisible(false);
227       }
228   }
229
230   void PlayerMainWindow::playpause()
231   {
232       // NB. There is no guarentee that our current state is the real current state.
233       // This is due to the polling frequency and possibility of user interaction directly on the server.
234       // Still this is probably better than nothing and our next real poll will set us straight again.
235       if (PAUSED == mCurrentStatus.state) {
236         mCurrentStatus.state = PLAYING;
237         pause();
238         updateUiWithCurrentStatus();
239       }
240       else if (PLAYING == mCurrentStatus.state) {
241         mCurrentStatus.state = PAUSED;
242         pause();
243         updateUiWithCurrentStatus();
244       }
245       else {
246         // could be STOP or UNKNOWN, either way there is no guarentee we will enter a playing state next.
247         // So don't update the current state or UI
248         // Ideally we would try to find a way to check the current state again but this could lead to an infinite loop!
249         play();
250       }
251   }
252   void PlayerMainWindow::play()
253   {
254       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_play")));
255   }
256   void PlayerMainWindow::stop()
257   {
258       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_stop")));
259   }
260   void PlayerMainWindow::pause()
261   {
262       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_pause")));
263   }
264   void PlayerMainWindow::previous()
265   {
266       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_previous")));
267   }
268   void PlayerMainWindow::next()
269   {
270       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_next")));
271   }
272   void PlayerMainWindow::fullscreen()
273   {
274       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=fullscreen")));
275   }
276   void PlayerMainWindow::volUp()
277   {
278       QUrl url = QUrl("http://"+mIp+"/requests/status.xml?command=volume");
279       url.addEncodedQueryItem(QByteArray("val"), QByteArray("%2B10"));
280       mNetManager->get(QNetworkRequest(url));
281   }
282   void PlayerMainWindow::volDown()
283   {
284       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=-10")));
285   }
286   void PlayerMainWindow::volMute()
287   {
288       this->mMuted = !this->mMuted;
289       if (this->mMuted) {
290           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=0")));
291       }
292       else {
293           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val="+QString::number(this->mVolume))));
294       }
295   }
296   void PlayerMainWindow::slide(int value)
297   {
298       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=seek&val="+QString::number(value)+"%25")));
299   }
300
301   void PlayerMainWindow::showConfig()
302   {
303       mTimer->stop();
304       // check for network
305       if (AppSettings::isConnected()) {
306           AccountDialog * dialog = new AccountDialog(this);
307           dialog->exec();
308
309            mIp= AccountDialog::currentIp();
310
311           mPlayListMainWindow->init();
312           mBrowserMainWindow->init();
313           mTimer->start(5000);
314           askStatus();
315       }
316       else {
317 #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
318         QMaemo5InformationBox::information(this, tr("Network unavailable!"), QMaemo5InformationBox::DefaultTimeout);
319 #endif
320         QTimer::singleShot(20000, this, SLOT(showConfig()));
321       }
322   }
323   void PlayerMainWindow::showAbout()
324   {
325
326       AboutDialog * dialog = new AboutDialog;
327       dialog->exec();
328
329   }
330
331   void PlayerMainWindow::askStatus()
332   {
333       //qDebug() << "Status requested. at:" << QTime::currentTime().toString("hh::mm:ss");
334       if (AppSettings::isConnected()) {
335           QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml")));
336           connect(reply,SIGNAL(readyRead()),this,SLOT(parseXmlStatus()));
337           connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(error(QNetworkReply::NetworkError)));
338       }
339       else {
340         showConfig(); // this will handle stopping and restarting the timer.
341       }
342   }
343
344   void PlayerMainWindow::parseXmlStatus()
345   {
346       QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
347       QDomDocument doc;
348       doc.setContent(reply->readAll());
349       delete reply;
350       QDomElement docElem = doc.documentElement();
351       // Get the raw values
352       int volume = docElem.namedItem("volume").toElement().text().toInt();
353       int length = docElem.namedItem("length").toElement().text().toInt();
354       int time = docElem.namedItem("time").toElement().text().toInt();
355       int position = docElem.namedItem("position").toElement().text().toInt();
356       int random = docElem.namedItem("random").toElement().text().toInt();
357       int loop = docElem.namedItem("loop").toElement().text().toInt();
358       int repeat = docElem.namedItem("repeat").toElement().text().toInt();
359       QString state = docElem.namedItem("state").toElement().text();
360       QDomNode infoNode =  docElem.namedItem("information");
361       QDomNode metaInfoNode =  infoNode.namedItem("meta-information");
362       QString title = metaInfoNode.namedItem("title").toElement().text().replace("\\\\", "\\");
363       // if it's a file style title fix it up
364       if (40 < title.length()) {
365           if (0 < title.lastIndexOf("\\")) {
366               title = title.right(title.length() - (title.lastIndexOf("\\") + 1));
367           }
368           else if (0 < title.lastIndexOf("/")) {
369               title = title.right(title.length() - (title.lastIndexOf("/") + 1));
370           }
371       }
372       QString artist = metaInfoNode.namedItem("artist").toElement().text();
373       QString album = metaInfoNode.namedItem("album").toElement().text();
374       QString now_playing = metaInfoNode.namedItem("now_playing").toElement().text();
375       QString art_url = metaInfoNode.namedItem("art_url").toElement().text();
376       // Populate the current status structure
377       // now would be a good time to work out if we are a new track / file or not.
378       // key if we are going to look for album art later
379       // for now we check length and title this will require further examination later
380       mCurrentStatus.newtrack = true;
381       if (mCurrentStatus.length == length && !mCurrentStatus.title.isNull() && 0 == QString::compare(mCurrentStatus.title, title)) {
382         mCurrentStatus.newtrack = false;
383       }
384       mCurrentStatus.volume = volume;
385       mCurrentStatus.length = length;
386       mCurrentStatus.time = time;
387       mCurrentStatus.position = position;
388       mCurrentStatus.random = (1 == random);
389       mCurrentStatus.loop = (1 == loop);
390       mCurrentStatus.repeat = (1 == repeat);
391       mCurrentStatus.title = title;
392       mCurrentStatus.artist = artist;
393       mCurrentStatus.album = album;
394       mCurrentStatus.nowplaying = now_playing;
395       mCurrentStatus.hasart = (!art_url.isNull() && !art_url.isEmpty());
396       if (!state.isNull() && !state.isEmpty()) {
397           if (0 == QString::compare("playing", state, Qt::CaseInsensitive)) {
398             mCurrentStatus.state = PLAYING;
399           }
400           else if (0 == QString::compare("paused", state, Qt::CaseInsensitive)) {
401             mCurrentStatus.state = PAUSED;
402           }
403           else if (0 == QString::compare("stop", state, Qt::CaseInsensitive)) {
404             mCurrentStatus.state = STOP;
405           }
406           else {
407             mCurrentStatus.state = UNKNOWN;
408           }
409       }
410       else {
411           mCurrentStatus.state = UNKNOWN;
412       }
413       // What's our mute status?
414       if (0 < mCurrentStatus.volume) {
415           this->mVolume = mCurrentStatus.volume;
416           this->mMuted = false;
417       }
418       else {
419           this->mMuted = true;
420       }
421       // Update the UI
422       updateUiWithCurrentStatus();
423
424   }
425
426   void PlayerMainWindow::updateUiWithCurrentStatus() {
427       // position
428       QTime timePosition(0,0,0) ;
429       timePosition =  timePosition.addSecs(mCurrentStatus.time);
430
431       ui->timeLabel->setText(timePosition.toString("h:mm:ss"));
432
433       // duration
434       if (0 < mCurrentStatus.length) {
435           QTime timeDuration(0,0,0) ;
436           timeDuration =  timeDuration.addSecs(mCurrentStatus.length);
437
438           ui->durationLabel->setText(timeDuration.toString("h:mm:ss"));
439       }
440       else {
441           ui->durationLabel->setText("0:00:00");
442       }
443
444
445       if (mCurrentStatus.position >= 0 && mCurrentStatus.position <= 100) {
446           ui->slider->blockSignals(true);
447           ui->slider->setValue(mCurrentStatus.position);
448           ui->slider->blockSignals(false);
449       }
450
451       ui->labelTitle->setText(mCurrentStatus.title);
452       ui->labelArtist->setText(mCurrentStatus.artist);
453       ui->labelAlbum->setText(mCurrentStatus.album);
454
455       if (PLAYING == mCurrentStatus.state) {
456           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_video_pause"));
457       }
458       else {
459           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
460       }
461
462       if (mCurrentStatus.newtrack) {
463           // potential actions:
464           //   rebuild display layout
465           //   retrieve album art
466           mHasImage = false;
467           QTimer::singleShot(500, mPlayListMainWindow, SLOT(requestPlayList()));
468       }
469       // Update the buttons on the playlist window
470       if (NULL != this->mPlayListMainWindow) {
471         this->mPlayListMainWindow->updateUiWithCurrentStatus(& mCurrentStatus);
472       }
473
474   }
475   void PlayerMainWindow::playlistIdUpdated(int id, bool hasart, QString extension) {
476       Q_UNUSED(extension);
477       if (hasart) {
478           getCoverArt(id);
479       }
480       else {
481           ui->labelArtLandscape->setVisible(false);
482           ui->labelArtPortrait->setVisible(false);
483           // could use a default graphic from extension here!
484           // setCoverArtFromPixmap();
485       }
486   }
487   void PlayerMainWindow::error(QNetworkReply::NetworkError code) {
488       qDebug() << "Error Code: " << code;
489   }
490   void PlayerMainWindow::readReady() {
491     QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
492     // append to buffer
493     mResponse += reply->readAll();
494   }
495   void PlayerMainWindow::finished(QNetworkReply * reply) {
496     // now we can call setCoverArt to process the full buffers
497     this->setCoverArt(mResponse);
498     // only interested in finished signals
499     disconnect(mNetManager,SIGNAL(finished(QNetworkReply *)),this,SLOT(finished(QNetworkReply *)));
500     delete reply;
501   }
502   void PlayerMainWindow::getCoverArt(int id) {
503       qDebug() << "getCoverArt id=!" << id;
504     mResponse.clear();
505     QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/art?id=" + QString::number(id))));
506     connect(reply,SIGNAL(readyRead()),this,SLOT(readReady()));
507     connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(error(QNetworkReply::NetworkError)));
508     connect(mNetManager,SIGNAL(finished(QNetworkReply *)),this,SLOT(finished(QNetworkReply *)));
509
510   }
511   void PlayerMainWindow::setCoverArt(const QByteArray data) {
512     QPixmap* image = new QPixmap();
513     if (image->loadFromData(data)) {
514         mHasImage = true;
515         ui->labelArtLandscape->setPixmap(image->scaledToHeight(120, Qt::SmoothTransformation));
516         ui->labelArtPortrait->setPixmap(image->scaledToHeight(310, Qt::SmoothTransformation));
517         if (mIsLandscape) {
518             ui->labelArtPortrait->setVisible(false);
519             ui->labelArtLandscape->setVisible(true);
520         }
521         else {
522             ui->labelArtLandscape->setVisible(false);
523             ui->labelArtPortrait->setVisible(true);
524         }
525     }
526     else {
527         qDebug() << "image load failed!";
528         qDebug() << "data.length" << data.length();
529         ui->labelArtPortrait->setVisible(false);
530         ui->labelArtLandscape->setVisible(false);
531     }
532     delete image;
533   }
534   void PlayerMainWindow::setCoverArtFromPixmap(QPixmap image) {
535     mHasImage = true;
536     ui->labelArtLandscape->setPixmap(image.scaledToHeight(120, Qt::SmoothTransformation));
537     ui->labelArtPortrait->setPixmap(image.scaledToHeight(320, Qt::SmoothTransformation));
538     if (mIsLandscape) {
539         ui->labelArtPortrait->setVisible(false);
540         ui->labelArtLandscape->setVisible(true);
541     }
542     else {
543         ui->labelArtLandscape->setVisible(false);
544         ui->labelArtPortrait->setVisible(true);
545     }
546   }
547