]> git.sur5r.net Git - minitube/blob - src/MediaView.cpp
Check for invokeMethod success
[minitube] / src / MediaView.cpp
1 #include "MediaView.h"
2 #include "playlist/PrettyItemDelegate.h"
3 #include "networkaccess.h"
4 #include "videowidget.h"
5 #include "minisplitter.h"
6
7 namespace The {
8     QMap<QString, QAction*>* globalActions();
9     QMap<QString, QMenu*>* globalMenus();
10     QNetworkAccessManager* networkAccessManager();
11 }
12
13 MediaView::MediaView(QWidget *parent) : QWidget(parent) {
14
15     reallyStopped = false;
16
17     QBoxLayout *layout = new QHBoxLayout();
18     layout->setMargin(0);
19
20     splitter = new MiniSplitter(this);
21     splitter->setChildrenCollapsible(false);
22
23     sortBar = new THBlackBar(this);
24     mostRelevantAction = new QAction(tr("Most relevant"), this);
25     QKeySequence keySequence(Qt::CTRL + Qt::Key_1);
26     mostRelevantAction->setShortcut(keySequence);
27     mostRelevantAction->setStatusTip(keySequence.toString(QKeySequence::NativeText));
28     addAction(mostRelevantAction);
29     connect(mostRelevantAction, SIGNAL(triggered()), this, SLOT(searchMostRelevant()), Qt::QueuedConnection);
30     sortBar->addAction(mostRelevantAction);
31     mostRecentAction = new QAction(tr("Most recent"), this);
32     keySequence = QKeySequence(Qt::CTRL + Qt::Key_2);
33     mostRecentAction->setShortcut(keySequence);
34     mostRecentAction->setStatusTip(keySequence.toString(QKeySequence::NativeText));
35     addAction(mostRecentAction);
36     connect(mostRecentAction, SIGNAL(triggered()), this, SLOT(searchMostRecent()), Qt::QueuedConnection);
37     sortBar->addAction(mostRecentAction);
38     mostViewedAction = new QAction(tr("Most viewed"), this);
39     keySequence = QKeySequence(Qt::CTRL + Qt::Key_3);
40     mostViewedAction->setShortcut(keySequence);
41     mostViewedAction->setStatusTip(keySequence.toString(QKeySequence::NativeText));
42     addAction(mostViewedAction);
43     connect(mostViewedAction, SIGNAL(triggered()), this, SLOT(searchMostViewed()), Qt::QueuedConnection);
44     sortBar->addAction(mostViewedAction);
45
46     listView = new QListView(this);
47     listView->setItemDelegate(new PrettyItemDelegate(this));
48     listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
49
50     // dragndrop
51     listView->setDragEnabled(true);
52     listView->setAcceptDrops(true);
53     listView->setDropIndicatorShown(true);
54     listView->setDragDropMode(QAbstractItemView::DragDrop);
55
56     // cosmetics
57     listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
58     listView->setFrameShape( QFrame::NoFrame );
59     listView->setAttribute(Qt::WA_MacShowFocusRect, false);
60     listView->setMinimumSize(320,240);
61
62     // respond to the user doubleclicking a playlist item
63     connect(listView, SIGNAL(activated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
64
65     listModel = new ListModel(this);
66     connect(listModel, SIGNAL(activeRowChanged(int)), this, SLOT(activeRowChanged(int)));
67     // needed to restore the selection after dragndrop
68     connect(listModel, SIGNAL(needSelectionFor(QList<Video*>)), this, SLOT(selectVideos(QList<Video*>)));
69     listView->setModel(listModel);
70
71     connect(listView->selectionModel(),
72             SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & )),
73             this, SLOT(selectionChanged ( const QItemSelection & , const QItemSelection & )));
74
75     playlistWidget = new PlaylistWidget(this, sortBar, listView);
76
77     splitter->addWidget(playlistWidget);
78
79     videoAreaWidget = new VideoAreaWidget(this);
80     videoAreaWidget->setMinimumSize(320,240);
81
82
83 #ifdef Q_WS_MAC
84     // mouse autohide does not work on the Mac (no mouseMoveEvent)
85     videoWidget = new Phonon::VideoWidget(this);
86 #else
87     videoWidget = new VideoWidget(this);
88 #endif
89
90     videoAreaWidget->setVideoWidget(videoWidget);
91     videoAreaWidget->setListModel(listModel);
92
93     loadingWidget = new LoadingWidget(this);
94     videoAreaWidget->setLoadingWidget(loadingWidget);
95
96     splitter->addWidget(videoAreaWidget);
97
98     layout->addWidget(splitter);
99     setLayout(layout);
100
101     // restore splitter state
102     QSettings settings;
103     splitter->restoreState(settings.value("splitter").toByteArray());
104
105     errorTimer = new QTimer(this);
106     errorTimer->setSingleShot(true);
107     errorTimer->setInterval(3000);
108     connect(errorTimer, SIGNAL(timeout()), SLOT(skipVideo()));
109
110     workaroundTimer = new QTimer(this);
111     workaroundTimer->setSingleShot(true);
112     workaroundTimer->setInterval(1000);
113     connect(workaroundTimer, SIGNAL(timeout()), SLOT(timerPlay()));
114
115 }
116
117 MediaView::~MediaView() {
118
119 }
120
121 void MediaView::initialize() {
122     connect(videoAreaWidget, SIGNAL(doubleClicked()), The::globalActions()->value("fullscreen"), SLOT(trigger()));
123     videoAreaWidget->setContextMenuPolicy(Qt::CustomContextMenu);
124     connect(videoAreaWidget, SIGNAL(customContextMenuRequested(QPoint)),
125             this, SLOT(showVideoContextMenu(QPoint)));
126 }
127
128 void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) {
129     this->mediaObject = mediaObject;
130     Phonon::createPath(this->mediaObject, videoWidget);
131     // connect(mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish()));
132     connect(mediaObject, SIGNAL(finished()), this, SLOT(skip()));
133     connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
134             this, SLOT(stateChanged(Phonon::State, Phonon::State)));
135     connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
136             this, SLOT(currentSourceChanged(Phonon::MediaSource)));
137     connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int)));
138 }
139
140 void MediaView::search(SearchParams *searchParams) {
141     reallyStopped = false;
142
143     this->searchParams = searchParams;
144
145     // start serching for videos
146     listModel->search(searchParams);
147
148     // this implies that the enum and the bar action order is the same
149     sortBar->setCheckedAction(searchParams->sortBy()-1);
150
151     listView->setFocus();
152
153     loadingWidget->clear();
154 }
155
156 void MediaView::disappear() {
157     timerPlayFlag = true;
158 }
159
160 void MediaView::handleError(QString message) {
161     videoAreaWidget->showError(message);
162     skippedVideo = listModel->activeVideo();
163     // recover from errors by skipping to the next video
164     errorTimer->start(2000);
165 }
166
167 void MediaView::stateChanged(Phonon::State newState, Phonon::State /*oldState*/)
168 {
169
170     // qDebug() << "Phonon state: " << newState << oldState;
171
172     switch (newState) {
173
174          case Phonon::ErrorState:
175         qDebug() << "Phonon error:" << mediaObject->errorString() << mediaObject->errorType();
176         handleError(mediaObject->errorString());
177         break;
178
179          case Phonon::PlayingState:
180         //qDebug("playing");
181         videoAreaWidget->showVideo();
182         break;
183
184          case Phonon::StoppedState:
185         //qDebug("stopped");
186         // play() has already been called when setting the source
187         // but Phonon on Linux needs a little more help to start playback
188         if (!reallyStopped) mediaObject->play();
189
190 #ifdef Q_WS_MAC
191         // Workaround for Mac playback start problem
192         if (!timerPlayFlag) {
193             workaroundTimer->start();
194         }
195 #endif
196
197         break;
198
199          case Phonon::PausedState:
200         //qDebug("paused");
201         break;
202
203          case Phonon::BufferingState:
204         //qDebug("buffering");
205         break;
206
207          case Phonon::LoadingState:
208         //qDebug("loading");
209         break;
210
211          default:
212         ;
213     }
214 }
215
216 void MediaView::pause() {
217     // qDebug() << "pause() called" << mediaObject->state();
218     switch( mediaObject->state() ) {
219     case Phonon::PlayingState:
220         mediaObject->pause();
221         break;
222     default:
223         mediaObject->play();
224         break;
225     }
226 }
227
228 void MediaView::stop() {
229     listModel->abortSearch();
230     reallyStopped = true;
231     mediaObject->stop();
232     workaroundTimer->stop();
233     errorTimer->stop();
234 }
235
236 void MediaView::activeRowChanged(int row) {
237     if (reallyStopped) return;
238
239     Video *video = listModel->videoAt(row);
240     if (!video) return;
241
242     // immediately show the loading widget
243     videoAreaWidget->showLoading(video);
244
245     // mediaObject->pause();
246
247     connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)));
248     // TODO handle signal in a proper slot and impl item error status
249     connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)));
250
251     video->loadStreamUrl();
252
253     // reset the timer flag
254     timerPlayFlag = false;
255
256     // video title in the statusbar
257     QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(qApp->topLevelWidgets().first());
258     if (mainWindow) mainWindow->statusBar()->showMessage(video->title());
259
260     // see you in gotStreamUrl...
261
262 }
263
264 void MediaView::gotStreamUrl(QUrl streamUrl) {
265     if (reallyStopped) return;
266
267     // go!
268     mediaObject->setCurrentSource(streamUrl);
269     mediaObject->play();
270
271     // ensure we always have 10 videos ahead
272     listModel->searchNeeded();
273
274     // ensure active item is visible
275     int row = listModel->activeRow();
276     if (row != -1) {
277         QModelIndex index = listModel->index(row, 0, QModelIndex());
278         listView->scrollTo(index, QAbstractItemView::EnsureVisible);
279     }
280
281     // HD indicator
282
283     // get the Video that sent the signal
284     Video *video = static_cast<Video *>(sender());
285     if (!video) {
286         qDebug() << "Cannot get sender";
287         return;
288     }
289     bool ret = QMetaObject::invokeMethod(qApp->topLevelWidgets().first(), "hdIndicator", Qt::DirectConnection, Q_ARG(bool, video->isHd()));
290     if (!ret) qDebug() << "hdIndicator invokeMethod failed";
291 }
292
293 void MediaView::itemActivated(const QModelIndex &index) {
294     if (listModel->rowExists(index.row()))
295         listModel->setActiveRow(index.row());
296     // the user doubleclicked on the "Search More" item
297     else listModel->searchMore();
298 }
299
300 void MediaView::aboutToFinish() {
301     /*
302     int nextRow = listModel->nextRow();
303     if (nextRow == -1) return;
304     Video* video = listModel->videoAt(nextRow);
305     QUrl streamUrl = video->streamUrl();
306     qDebug() << "Enqueing" << streamUrl;
307     mediaObject->enqueue(streamUrl);
308     */
309 }
310
311 void MediaView::currentSourceChanged(const Phonon::MediaSource source) {
312     qDebug() << source.url().toString();
313 }
314
315
316 void MediaView::skipVideo() {
317     // skippedVideo is useful for DELAYED skip operations
318     // in order to be sure that we're skipping the video we wanted
319     // and not another one
320     if (skippedVideo) {
321         if (listModel->activeVideo() != skippedVideo) {
322             qDebug() << "Skip of video canceled";
323             return;
324         }
325         int nextRow = listModel->rowForVideo(skippedVideo);
326         nextRow++;
327         if (nextRow == -1) return;
328         listModel->setActiveRow(nextRow);
329     }
330 }
331
332 void MediaView::skip() {
333     int nextRow = listModel->nextRow();
334     if (nextRow == -1) return;
335     listModel->setActiveRow(nextRow);
336 }
337
338 void MediaView::openWebPage() {
339     Video* video = listModel->activeVideo();
340     if (!video) return;
341     mediaObject->pause();
342     QDesktopServices::openUrl(video->webpage());
343 }
344
345 void MediaView::removeSelected() {
346     if (!listView->selectionModel()->hasSelection()) return;
347     QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
348     listModel->removeIndexes(indexes);
349 }
350
351 void MediaView::selectVideos(QList<Video*> videos) {
352     foreach (Video *video, videos) {
353         QModelIndex index = listModel->indexForVideo(video);
354         listView->selectionModel()->select(index, QItemSelectionModel::Select);
355         listView->scrollTo(index, QAbstractItemView::EnsureVisible);
356     }
357 }
358
359 void MediaView::selectionChanged(const QItemSelection & /*selected*/, const QItemSelection & /*deselected*/) {
360     const bool gotSelection = listView->selectionModel()->hasSelection();
361     The::globalActions()->value("remove")->setEnabled(gotSelection);
362     The::globalActions()->value("moveUp")->setEnabled(gotSelection);
363     The::globalActions()->value("moveDown")->setEnabled(gotSelection);
364 }
365
366 void MediaView::moveUpSelected() {
367     if (!listView->selectionModel()->hasSelection()) return;
368     QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
369     listModel->move(indexes, true);
370 }
371
372 void MediaView::moveDownSelected() {
373     if (!listView->selectionModel()->hasSelection()) return;
374     QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
375     listModel->move(indexes, false);
376 }
377
378 void MediaView::showVideoContextMenu(QPoint point) {
379     The::globalMenus()->value("video")->popup(videoWidget->mapToGlobal(point));
380 }
381
382 void MediaView::searchMostRelevant() {
383     searchParams->setSortBy(SearchParams::SortByRelevance);
384     search(searchParams);
385 }
386
387 void MediaView::searchMostRecent() {
388     searchParams->setSortBy(SearchParams::SortByNewest);
389     search(searchParams);
390 }
391
392 void MediaView::searchMostViewed() {
393     searchParams->setSortBy(SearchParams::SortByViewCount);
394     search(searchParams);
395 }
396
397 void MediaView::setPlaylistVisible(bool visible) {
398     playlistWidget->setVisible(visible);
399 }
400
401 void MediaView::timerPlay() {
402     // qDebug() << mediaObject->currentTime();
403     // Workaround Phonon bug on Mac OSX
404     if (mediaObject->currentTime() <= 0 && mediaObject->state() == Phonon::PlayingState) {
405         mediaObject->pause();
406         mediaObject->play();
407     }
408 }
409
410 void MediaView::saveSplitterState() {
411     QSettings settings;
412     settings.setValue("splitter", splitter->saveState());
413 }