1157c66a7e11986d4fb72fcfd6588384f43317a6
[vlc-remote] / 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 "vlcstatus.h"
27
28   PlayerMainWindow::PlayerMainWindow(QWidget *parent) :
29           QMainWindow(parent),
30           ui(new Ui::PlayerMainWindow)
31   {
32       ui->setupUi(this);
33       setWindowTitle("Vlc remote");
34
35
36
37       mTimer = new QTimer(this);
38       mNetManager = new QNetworkAccessManager(this);
39       mPlayListMainWindow = new PlayListMainWindow;
40       mBrowserMainWindow = new BrowseMainWindow;
41
42       mVolume = 100;
43       mMuted = false;
44
45       mIsLandscape = true;
46
47       ui->playlistButton->setIcon(QIcon::fromTheme("notes_bullets"));
48       ui->browseButton->setIcon(QIcon::fromTheme("filemanager_media_folder"));
49
50       ui->previousButton->setIcon(QIcon::fromTheme("pdf_viewer_first_page"));
51       ui->nextButton->setIcon(QIcon::fromTheme("pdf_viewer_last_page"));
52       ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
53       ui->stopButton->setIcon(QIcon::fromTheme("camera_video_stop"));
54       //ui->pauseButton->setIcon(QIcon::fromTheme("camera_video_pause"));
55       ui->fullscreenButton->setIcon(QIcon::fromTheme("general_fullsize"));
56       ui->volDown->setIcon(QIcon::fromTheme("statusarea_volumelevel1"));
57       ui->volUp->setIcon(QIcon::fromTheme("statusarea_volumelevel4"));
58       ui->volMute->setIcon(QIcon::fromTheme("statusarea_volume_mute"));
59
60       ui->labelArtPortrait->setVisible(false);
61       ui->labelArtLandscape->setVisible(false);
62
63       ui->labelTitle->setTextFormat(Qt::RichText);
64       ui->labelArtist->setTextFormat(Qt::RichText);
65       ui->labelAlbum->setTextFormat(Qt::RichText);
66
67
68   #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
69
70       mPlayListMainWindow->setParent(this);
71       mPlayListMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow);
72       setAttribute(Qt::WA_Maemo5StackedWindow);
73       mPlayListMainWindow->setWindowFlags(mPlayListMainWindow->windowFlags() | Qt::Window);
74
75       mBrowserMainWindow->setParent(this);
76       mBrowserMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow);
77       setAttribute(Qt::WA_Maemo5StackedWindow);
78       mBrowserMainWindow->setWindowFlags(mBrowserMainWindow->windowFlags() | Qt::Window);
79
80       connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(orientationChanged()));
81
82   #endif
83
84       connect(mTimer,SIGNAL(timeout()),this,SLOT(askStatus()));
85       connect(ui->actionConfiguration,SIGNAL(triggered()),this,SLOT(showConfig()));
86       connect(ui->actionAbout,SIGNAL(triggered()),this,SLOT(showAbout()));
87       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(show()));
88       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(showPlayList()));
89       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(show()));
90       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(showCurrentDirectory()));
91
92       connect(ui->playpauseButton,SIGNAL(clicked()),this,SLOT(playpause()));
93       connect(ui->stopButton,SIGNAL(clicked()),this,SLOT(stop()));
94       //connect(ui->pauseButton,SIGNAL(clicked()),this,SLOT(playpause()));
95       connect(ui->previousButton,SIGNAL(clicked()),this,SLOT(previous()));
96       connect(ui->nextButton,SIGNAL(clicked()),this,SLOT(next()));
97       connect(ui->fullscreenButton,SIGNAL(clicked()),this,SLOT(fullscreen()));
98       connect(ui->volUp,SIGNAL(clicked()),this,SLOT(volUp()));
99       connect(ui->volDown,SIGNAL(clicked()),this,SLOT(volDown()));
100       connect(ui->volMute,SIGNAL(clicked()),this,SLOT(volMute()));
101       connect(ui->slider,SIGNAL(sliderMoved(int)),this,SLOT(slide(int)));
102
103       connect(mPlayListMainWindow, SIGNAL(idUpdated(int,bool,QString)), this, SLOT(playlistIdUpdated(int, bool, QString)));
104
105
106       // check if last used connection is still valid or showConfig
107       QSettings settings;
108       QString last_ip = AccountDialog::currentIp();
109       if (!last_ip.isNull() && !last_ip.isEmpty()) {
110           QTcpSocket * socket = new QTcpSocket;
111           if(last_ip.contains(":"))
112           {
113               QStringList hostSplit = last_ip.split(":");
114               QString ip   = hostSplit.at(0);
115               QString port = hostSplit.at(1);
116               socket->connectToHost(ip,port.toInt());
117           }
118           else {
119               socket->connectToHost(last_ip,8080);
120           }
121           if (!socket->waitForConnected(1000)) {
122                  showConfig();
123              }
124           else {
125               mIp= last_ip;
126
127              mPlayListMainWindow->init();
128              mBrowserMainWindow->init();
129              mTimer->start(5000);
130              askStatus();
131           }
132           delete socket;
133       }
134       else {
135         showConfig();
136       }
137
138
139   }
140   
141
142   PlayerMainWindow::~PlayerMainWindow()
143   {
144       delete ui;
145   }
146
147   void PlayerMainWindow::changeEvent(QEvent *e)
148   {
149       QMainWindow::changeEvent(e);
150       switch (e->type()) {
151       case QEvent::LanguageChange:
152           ui->retranslateUi(this);
153           break;
154       default:
155           break;
156       }
157   }
158
159   void PlayerMainWindow::orientationChanged() {
160       QRect screenGeometry = QApplication::desktop()->screenGeometry();
161       mIsLandscape = (screenGeometry.width() > screenGeometry.height());
162       if (mHasImage) {
163           if (mIsLandscape) {
164               ui->labelArtPortrait->setVisible(false);
165               ui->labelArtLandscape->setVisible(true);
166           }
167           else {
168               ui->labelArtLandscape->setVisible(false);
169               ui->labelArtPortrait->setVisible(true);
170           }
171       }
172       else {
173           ui->labelArtLandscape->setVisible(false);
174           ui->labelArtPortrait->setVisible(false);
175       }
176   }
177
178   void PlayerMainWindow::playpause()
179   {
180       // NB. There is no guarentee that our current state is the real current state.
181       // This is due to the polling frequency and possibility of user interaction directly on the server.
182       // Still this is probably better than nothing and our next real poll will set us straight again.
183       if (PAUSED == mCurrentStatus.state) {
184         mCurrentStatus.state = PLAYING;
185         pause();
186         updateUiWithCurrentStatus();
187       }
188       else if (PLAYING == mCurrentStatus.state) {
189         mCurrentStatus.state = PAUSED;
190         pause();
191         updateUiWithCurrentStatus();
192       }
193       else {
194         // could be STOP or UNKNOWN, either way there is no guarentee we will enter a playing state next.
195         // So don't update the current state or UI
196         // Ideally we would try to find a way to check the current state again but this could lead to an infinite loop!
197         play();
198       }
199   }
200   void PlayerMainWindow::play()
201   {
202       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_play")));
203   }
204   void PlayerMainWindow::stop()
205   {
206       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_stop")));
207   }
208   void PlayerMainWindow::pause()
209   {
210       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_pause")));
211   }
212   void PlayerMainWindow::previous()
213   {
214       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_previous")));
215   }
216   void PlayerMainWindow::next()
217   {
218       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_next")));
219   }
220   void PlayerMainWindow::fullscreen()
221   {
222       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=fullscreen")));
223   }
224   void PlayerMainWindow::volUp()
225   {
226       QUrl url = QUrl("http://"+mIp+"/requests/status.xml?command=volume");
227       url.addEncodedQueryItem(QByteArray("val"), QByteArray("%2B20"));
228       mNetManager->get(QNetworkRequest(url));
229   }
230   void PlayerMainWindow::volDown()
231   {
232       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=-20")));
233   }
234   void PlayerMainWindow::volMute()
235   {
236       this->mMuted = !this->mMuted;
237       if (this->mMuted) {
238           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=0")));
239       }
240       else {
241           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val="+QString::number(this->mVolume))));
242       }
243   }
244   void PlayerMainWindow::slide(int value)
245   {
246       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=seek&val="+QString::number(value)+"%25")));
247   }
248
249   void PlayerMainWindow::showConfig()
250   {
251       mTimer->stop();
252       AccountDialog * dialog = new AccountDialog;
253       dialog->exec();
254      
255        mIp= AccountDialog::currentIp();
256
257       mPlayListMainWindow->init();
258       mBrowserMainWindow->init();
259       mTimer->start(5000);
260       askStatus();
261   }
262   void PlayerMainWindow::showAbout()
263   {
264
265       AboutDialog * dialog = new AboutDialog;
266       dialog->exec();
267
268   }
269
270   void PlayerMainWindow::askStatus()
271   {
272
273       QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml")));
274       connect(reply,SIGNAL(readyRead()),this,SLOT(parseXmlStatus()));
275   }
276
277   void PlayerMainWindow::parseXmlStatus()
278   {
279       QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
280       QDomDocument doc;
281       doc.setContent(reply->readAll());
282       delete reply;
283       QDomElement docElem = doc.documentElement();
284       // Get the raw values
285       int volume = docElem.namedItem("volume").toElement().text().toInt();
286       int length = docElem.namedItem("length").toElement().text().toInt();
287       int time = docElem.namedItem("time").toElement().text().toInt();
288       int position = docElem.namedItem("position").toElement().text().toInt();
289       int random = docElem.namedItem("random").toElement().text().toInt();
290       int loop = docElem.namedItem("loop").toElement().text().toInt();
291       int repeat = docElem.namedItem("repeat").toElement().text().toInt();
292       QString state = docElem.namedItem("state").toElement().text();
293       QDomNode infoNode =  docElem.namedItem("information");
294       QDomNode metaInfoNode =  infoNode.namedItem("meta-information");
295       QString title = metaInfoNode.namedItem("title").toElement().text().replace("\\\\", "\\");
296       // if it's a file style title fix it up
297       if (40 < title.length()) {
298           if (0 < title.lastIndexOf("\\")) {
299               title = title.right(title.length() - (title.lastIndexOf("\\") + 1));
300           }
301           else if (0 < title.lastIndexOf("/")) {
302               title = title.right(title.length() - (title.lastIndexOf("/") + 1));
303           }
304       }
305       QString artist = metaInfoNode.namedItem("artist").toElement().text();
306       QString album = metaInfoNode.namedItem("album").toElement().text();
307       QString now_playing = metaInfoNode.namedItem("now_playing").toElement().text();
308       QString art_url = metaInfoNode.namedItem("art_url").toElement().text();
309       // Populate the current status structure
310       // now would be a good time to work out if we are a new track / file or not.
311       // key if we are going to look for album art later
312       // for now we check length and title this will require further examination later
313       mCurrentStatus.newtrack = true;
314       if (mCurrentStatus.length == length && !mCurrentStatus.title.isNull() && 0 == QString::compare(mCurrentStatus.title, title)) {
315         mCurrentStatus.newtrack = false;
316       }
317       mCurrentStatus.volume = volume;
318       mCurrentStatus.length = length;
319       mCurrentStatus.time = time;
320       mCurrentStatus.position = position;
321       mCurrentStatus.random = (1 == random);
322       mCurrentStatus.loop = (1 == loop);
323       mCurrentStatus.repeat = (1 == repeat);
324       mCurrentStatus.title = title;
325       mCurrentStatus.artist = artist;
326       mCurrentStatus.album = album;
327       mCurrentStatus.nowplaying = now_playing;
328       mCurrentStatus.hasart = (!art_url.isNull() && !art_url.isEmpty());
329       if (!state.isNull() && !state.isEmpty()) {
330           if (0 == QString::compare("playing", state, Qt::CaseInsensitive)) {
331             mCurrentStatus.state = PLAYING;
332           }
333           else if (0 == QString::compare("paused", state, Qt::CaseInsensitive)) {
334             mCurrentStatus.state = PAUSED;
335           }
336           else if (0 == QString::compare("stop", state, Qt::CaseInsensitive)) {
337             mCurrentStatus.state = STOP;
338           }
339           else {
340             mCurrentStatus.state = UNKNOWN;
341           }
342       }
343       else {
344           mCurrentStatus.state = UNKNOWN;
345       }
346       // What's our mute status?
347       if (0 < mCurrentStatus.volume) {
348           this->mVolume = mCurrentStatus.volume;
349           this->mMuted = false;
350       }
351       else {
352           this->mMuted = true;
353       }
354       // Update the UI
355       updateUiWithCurrentStatus();
356
357   }
358
359   void PlayerMainWindow::updateUiWithCurrentStatus() {
360       // position
361       QTime timePosition(0,0,0) ;
362       timePosition =  timePosition.addSecs(mCurrentStatus.time);
363
364       ui->timeLabel->setText(timePosition.toString("h:mm:ss"));
365
366       // duration
367       if (0 < mCurrentStatus.length) {
368           QTime timeDuration(0,0,0) ;
369           timeDuration =  timeDuration.addSecs(mCurrentStatus.length);
370
371           ui->durationLabel->setText(timeDuration.toString("h:mm:ss"));
372       }
373       else {
374           ui->durationLabel->setText("0:00:00");
375       }
376
377
378       if (mCurrentStatus.position >= 0 && mCurrentStatus.position <= 100)
379           ui->slider->setValue(mCurrentStatus.position);
380
381       ui->labelTitle->setText(mCurrentStatus.title);
382       ui->labelArtist->setText(mCurrentStatus.artist);
383       ui->labelAlbum->setText(mCurrentStatus.album);
384
385       if (PLAYING == mCurrentStatus.state) {
386           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_video_pause"));
387       }
388       else {
389           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
390       }
391
392       if (mCurrentStatus.newtrack) {
393           // potential actions:
394           //   rebuild display layout
395           //   retrieve album art
396           mHasImage = false;
397           mPlayListMainWindow->requestPlayList();
398       }
399       // Update the buttons on the playlist window
400       if (NULL != this->mPlayListMainWindow) {
401         this->mPlayListMainWindow->updateUiWithCurrentStatus(& mCurrentStatus);
402       }
403
404   }
405   void PlayerMainWindow::playlistIdUpdated(int id, bool hasart, QString extension) {
406       if (hasart) {
407           getCoverArt(id);
408       }
409       else {
410           ui->labelArtLandscape->setVisible(false);
411           ui->labelArtPortrait->setVisible(false);
412           // could use a default graphic here!
413           // setCoverArtFromPixmap();
414       }
415   }
416   void PlayerMainWindow::readReady() {
417     QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
418     // append to buffer
419     mResponse += reply->readAll();
420   }
421   void PlayerMainWindow::finished(QNetworkReply * reply) {
422     // now we can call setCoverArt to process the full buffers
423     this->setCoverArt(mResponse);
424     // only interested in finished signals
425     disconnect(mNetManager,SIGNAL(finished(QNetworkReply *)),this,SLOT(finished(QNetworkReply *)));
426   }
427   void PlayerMainWindow::getCoverArt(int id) {
428     mResponse.clear();
429     QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/art?id=" + QString::number(id))));
430     connect(reply,SIGNAL(readyRead()),this,SLOT(readReady()));
431     connect(mNetManager,SIGNAL(finished(QNetworkReply *)),this,SLOT(finished(QNetworkReply *)));
432
433   }
434   void PlayerMainWindow::setCoverArt(const QByteArray data) {
435     QPixmap* image = new QPixmap();
436     if (image->loadFromData(data)) {
437         mHasImage = true;
438         ui->labelArtLandscape->setPixmap(image->scaledToHeight(120, Qt::SmoothTransformation));
439         ui->labelArtPortrait->setPixmap(image->scaledToHeight(310, Qt::SmoothTransformation));
440         if (mIsLandscape) {
441             ui->labelArtPortrait->setVisible(false);
442             ui->labelArtLandscape->setVisible(true);
443         }
444         else {
445             ui->labelArtLandscape->setVisible(false);
446             ui->labelArtPortrait->setVisible(true);
447         }
448     }
449     else {
450         ui->labelArtPortrait->setVisible(false);
451         ui->labelArtLandscape->setVisible(false);
452     }
453   }
454   void PlayerMainWindow::setCoverArtFromPixmap(QPixmap image) {
455     mHasImage = true;
456     ui->labelArtLandscape->setPixmap(image.scaledToHeight(120, Qt::SmoothTransformation));
457     ui->labelArtPortrait->setPixmap(image.scaledToHeight(310, Qt::SmoothTransformation));
458     if (mIsLandscape) {
459         ui->labelArtPortrait->setVisible(false);
460         ui->labelArtLandscape->setVisible(true);
461     }
462     else {
463         ui->labelArtLandscape->setVisible(false);
464         ui->labelArtPortrait->setVisible(true);
465     }
466   }
467