]> git.sur5r.net Git - minitube/blob - src/MediaView.cpp
Imported Upstream version 1.7
[minitube] / src / MediaView.cpp
1 #include "MediaView.h"
2 #include "playlistview.h"
3 #include "playlist/PrettyItemDelegate.h"
4 #include "networkaccess.h"
5 #include "videowidget.h"
6 #include "minisplitter.h"
7 #include "constants.h"
8 #include "downloadmanager.h"
9 #include "downloaditem.h"
10 #include "MainWindow.h"
11 #include "temporary.h"
12
13 namespace The {
14 NetworkAccess* http();
15 }
16
17 namespace The {
18 QMap<QString, QAction*>* globalActions();
19 QMap<QString, QMenu*>* globalMenus();
20 QNetworkAccessManager* networkAccessManager();
21 }
22
23 MediaView::MediaView(QWidget *parent) : QWidget(parent) {
24
25     reallyStopped = false;
26     downloadItem = 0;
27
28     QBoxLayout *layout = new QVBoxLayout();
29     layout->setMargin(0);
30
31     splitter = new MiniSplitter(this);
32     splitter->setChildrenCollapsible(false);
33
34     sortBar = new SegmentedControl(this);
35     mostRelevantAction = new QAction(tr("Most relevant"), this);
36     QKeySequence keySequence(Qt::CTRL + Qt::Key_1);
37     mostRelevantAction->setShortcut(keySequence);
38     mostRelevantAction->setStatusTip(mostRelevantAction->text() + " (" + keySequence.toString(QKeySequence::NativeText) + ")");
39     addAction(mostRelevantAction);
40     connect(mostRelevantAction, SIGNAL(triggered()), this, SLOT(searchMostRelevant()), Qt::QueuedConnection);
41     sortBar->addAction(mostRelevantAction);
42     mostRecentAction = new QAction(tr("Most recent"), this);
43     keySequence = QKeySequence(Qt::CTRL + Qt::Key_2);
44     mostRecentAction->setShortcut(keySequence);
45     mostRecentAction->setStatusTip(mostRecentAction->text() + " (" + keySequence.toString(QKeySequence::NativeText) + ")");
46     addAction(mostRecentAction);
47     connect(mostRecentAction, SIGNAL(triggered()), this, SLOT(searchMostRecent()), Qt::QueuedConnection);
48     sortBar->addAction(mostRecentAction);
49     mostViewedAction = new QAction(tr("Most viewed"), this);
50     keySequence = QKeySequence(Qt::CTRL + Qt::Key_3);
51     mostViewedAction->setShortcut(keySequence);
52     mostViewedAction->setStatusTip(mostViewedAction->text() + " (" + keySequence.toString(QKeySequence::NativeText) + ")");
53     addAction(mostViewedAction);
54     connect(mostViewedAction, SIGNAL(triggered()), this, SLOT(searchMostViewed()), Qt::QueuedConnection);
55     sortBar->addAction(mostViewedAction);
56
57     listView = new PlaylistView(this);
58     listView->setItemDelegate(new PrettyItemDelegate(this));
59     listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
60
61     // dragndrop
62     listView->setDragEnabled(true);
63     listView->setAcceptDrops(true);
64     listView->setDropIndicatorShown(true);
65     listView->setDragDropMode(QAbstractItemView::DragDrop);
66
67     // cosmetics
68     listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
69     listView->setFrameShape( QFrame::NoFrame );
70     listView->setAttribute(Qt::WA_MacShowFocusRect, false);
71     listView->setMinimumSize(320,240);
72     listView->setUniformItemSizes(true);
73
74     // respond to the user doubleclicking a playlist item
75     connect(listView, SIGNAL(activated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
76
77     listModel = new ListModel(this);
78     connect(listModel, SIGNAL(activeRowChanged(int)), this, SLOT(activeRowChanged(int)));
79     // needed to restore the selection after dragndrop
80     connect(listModel, SIGNAL(needSelectionFor(QList<Video*>)), this, SLOT(selectVideos(QList<Video*>)));
81     listView->setModel(listModel);
82
83     connect(listView->selectionModel(),
84             SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & )),
85             this, SLOT(selectionChanged ( const QItemSelection & , const QItemSelection & )));
86
87     connect(listView, SIGNAL(authorPushed(QModelIndex)), SLOT(authorPushed(QModelIndex)));
88
89     playlistWidget = new PlaylistWidget(this, sortBar, listView);
90
91     splitter->addWidget(playlistWidget);
92
93     videoAreaWidget = new VideoAreaWidget(this);
94     videoAreaWidget->setMinimumSize(320,240);
95
96 #ifdef APP_MAC_NO
97     // mouse autohide does not work on the Mac (no mouseMoveEvent)
98     videoWidget = new Phonon::VideoWidget(this);
99 #else
100     videoWidget = new VideoWidget(this);
101 #endif
102
103     videoAreaWidget->setVideoWidget(videoWidget);
104     videoAreaWidget->setListModel(listModel);
105
106     loadingWidget = new LoadingWidget(this);
107     videoAreaWidget->setLoadingWidget(loadingWidget);
108
109     splitter->addWidget(videoAreaWidget);
110
111     layout->addWidget(splitter);
112     setLayout(layout);
113
114     splitter->setStretchFactor(0, 1);
115     splitter->setStretchFactor(1, 6);
116
117     // restore splitter state
118     QSettings settings;
119     splitter->restoreState(settings.value("splitter").toByteArray());
120
121     errorTimer = new QTimer(this);
122     errorTimer->setSingleShot(true);
123     errorTimer->setInterval(3000);
124     connect(errorTimer, SIGNAL(timeout()), SLOT(skipVideo()));
125
126     workaroundTimer = new QTimer(this);
127     workaroundTimer->setSingleShot(true);
128     workaroundTimer->setInterval(3000);
129     connect(workaroundTimer, SIGNAL(timeout()), SLOT(timerPlay()));
130
131 #ifdef APP_DEMO
132     demoTimer = new QTimer(this);
133     demoTimer->setSingleShot(true);
134     connect(demoTimer, SIGNAL(timeout()), SLOT(demoMessage()));
135 #endif
136
137 }
138
139 void MediaView::initialize() {
140     connect(videoAreaWidget, SIGNAL(doubleClicked()), The::globalActions()->value("fullscreen"), SLOT(trigger()));
141
142     /*
143     videoAreaWidget->setContextMenuPolicy(Qt::CustomContextMenu);
144     connect(videoAreaWidget, SIGNAL(customContextMenuRequested(QPoint)),
145             this, SLOT(showVideoContextMenu(QPoint)));
146             */
147 }
148
149 void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) {
150     this->mediaObject = mediaObject;
151     Phonon::createPath(this->mediaObject, videoWidget);
152     connect(mediaObject, SIGNAL(finished()), this, SLOT(playbackFinished()));
153     connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
154             this, SLOT(stateChanged(Phonon::State, Phonon::State)));
155     connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
156             this, SLOT(currentSourceChanged(Phonon::MediaSource)));
157     // connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int)));
158 }
159
160 void MediaView::search(SearchParams *searchParams) {
161     reallyStopped = false;
162
163 #ifdef APP_DEMO
164     demoTimer->stop();
165 #endif
166     workaroundTimer->stop();
167     errorTimer->stop();
168
169     this->searchParams = searchParams;
170
171     // start serching for videos
172     listModel->search(searchParams);
173
174     // this implies that the enum and the bar action order is the same
175     sortBar->setCheckedAction(searchParams->sortBy()-1);
176
177     listView->setFocus();
178
179     QString keyword = searchParams->keywords();
180     QString display = keyword;
181     if (keyword.startsWith("http://") || keyword.startsWith("https://")) {
182         int separator = keyword.indexOf("|");
183         if (separator > 0 && separator + 1 < keyword.length()) {
184             display = keyword.mid(separator+1);
185         }
186     }
187
188 }
189
190 void MediaView::appear() {
191     listView->setFocus();
192 }
193
194 void MediaView::disappear() {
195     timerPlayFlag = true;
196 }
197
198 void MediaView::handleError(QString /* message */) {
199
200     QTimer::singleShot(100, this, SLOT(startPlaying()));
201
202     /*
203     videoAreaWidget->showError(message);
204     skippedVideo = listModel->activeVideo();
205     // recover from errors by skipping to the next video
206     errorTimer->start(2000);
207     */
208 }
209
210 void MediaView::stateChanged(Phonon::State newState, Phonon::State /*oldState*/)
211 {
212
213     // qDebug() << "Phonon state: " << newState << oldState;
214     // slider->setEnabled(newState == Phonon::PlayingState);
215
216     switch (newState) {
217
218     case Phonon::ErrorState:
219         qDebug() << "Phonon error:" << mediaObject->errorString() << mediaObject->errorType();
220         if (mediaObject->errorType() == Phonon::FatalError)
221             handleError(mediaObject->errorString());
222         break;
223
224     case Phonon::PlayingState:
225         // qDebug("playing");
226         videoAreaWidget->showVideo();
227         break;
228
229     case Phonon::StoppedState:
230         // qDebug("stopped");
231         // play() has already been called when setting the source
232         // but Phonon on Linux needs a little more help to start playback
233         // if (!reallyStopped) mediaObject->play();
234
235 #ifdef APP_MAC
236         // Workaround for Mac playback start problem
237         if (!timerPlayFlag) {
238             // workaroundTimer->start();
239         }
240 #endif
241
242         break;
243
244     case Phonon::PausedState:
245         qDebug("paused");
246         break;
247
248     case Phonon::BufferingState:
249         qDebug("buffering");
250         break;
251
252     case Phonon::LoadingState:
253         qDebug("loading");
254         break;
255
256     }
257 }
258
259 void MediaView::pause() {
260     // qDebug() << "pause() called" << mediaObject->state();
261
262     switch( mediaObject->state() ) {
263     case Phonon::PlayingState:
264         mediaObject->pause();
265         break;
266     default:
267         mediaObject->play();
268         break;
269     }
270
271 }
272
273 QRegExp MediaView::wordRE(QString s) {
274     return QRegExp("\\W" + s + "\\W?", Qt::CaseInsensitive);
275 }
276
277 void MediaView::stop() {
278     listModel->abortSearch();
279     reallyStopped = true;
280     mediaObject->stop();
281     videoAreaWidget->clear();
282     workaroundTimer->stop();
283     errorTimer->stop();
284     listView->selectionModel()->clearSelection();
285     if (downloadItem) {
286         downloadItem->stop();
287         delete downloadItem;
288         downloadItem = 0;
289     }
290 }
291
292 void MediaView::activeRowChanged(int row) {
293     if (reallyStopped) return;
294
295     Video *video = listModel->videoAt(row);
296     if (!video) return;
297
298     // now that we have a new video to play
299     // stop all the timers
300     workaroundTimer->stop();
301     errorTimer->stop();
302
303     mediaObject->pause();
304     if (downloadItem) {
305         downloadItem->stop();
306         delete downloadItem;
307         downloadItem = 0;
308     }
309     // slider->setMinimum(0);
310
311     // immediately show the loading widget
312     videoAreaWidget->showLoading(video);
313
314     connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)), Qt::UniqueConnection);
315     // TODO handle signal in a proper slot and impl item error status
316     connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)), Qt::UniqueConnection);
317
318     video->loadStreamUrl();
319
320     // reset the timer flag
321     timerPlayFlag = false;
322
323     // video title in the statusbar
324     QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
325     if (mainWindow) mainWindow->statusBar()->showMessage(video->title());
326
327
328     // ensure active item is visible
329     // int row = listModel->activeRow();
330     if (row != -1) {
331         QModelIndex index = listModel->index(row, 0, QModelIndex());
332         listView->scrollTo(index, QAbstractItemView::EnsureVisible);
333     }
334
335     // enable/disable actions
336     The::globalActions()->value("download")->setEnabled(DownloadManager::instance()->itemForVideo(video) == 0);
337     The::globalActions()->value("skip")->setEnabled(true);
338     The::globalActions()->value("previous")->setEnabled(row > 0);
339     The::globalActions()->value("stopafterthis")->setEnabled(true);
340
341     // see you in gotStreamUrl...
342
343 }
344
345 void MediaView::gotStreamUrl(QUrl streamUrl) {
346     if (reallyStopped) return;
347
348     Video *video = static_cast<Video *>(sender());
349     if (!video) {
350         qDebug() << "Cannot get sender";
351         return;
352     }
353     video->disconnect(this);
354
355     QString tempFile = Temporary::filename();
356
357     Video *videoCopy = video->clone();
358     if (downloadItem) {
359         downloadItem->stop();
360         delete downloadItem;
361     }
362     downloadItem = new DownloadItem(videoCopy, streamUrl, tempFile, this);
363     connect(downloadItem, SIGNAL(statusChanged()), SLOT(downloadStatusChanged()), Qt::UniqueConnection);
364     // connect(downloadItem, SIGNAL(progress(int)), SLOT(downloadProgress(int)));
365     connect(downloadItem, SIGNAL(bufferProgress(int)), loadingWidget, SLOT(bufferStatus(int)), Qt::UniqueConnection);
366     // connect(downloadItem, SIGNAL(finished()), SLOT(itemFinished()));
367     connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)), Qt::UniqueConnection);
368     connect(downloadItem, SIGNAL(error(QString)), SLOT(handleError(QString)), Qt::UniqueConnection);
369     downloadItem->start();
370
371 }
372
373 /*
374 void MediaView::downloadProgress(int percent) {
375     MainWindow* mainWindow = dynamic_cast<MainWindow*>(window());
376
377     mainWindow->getSeekSlider()->setStyleSheet(" QSlider::groove:horizontal {"
378         "border: 1px solid #999999;"
379         // "border-left: 50px solid rgba(255, 0, 0, 128);"
380         "height: 8px;"
381         "background: qlineargradient(x1:0, y1:0, x2:.5, y2:0, stop:0 rgba(255, 0, 0, 92), stop:"
382         + QString::number(percent/100.0) +
383
384         " rgba(255, 0, 0, 92), stop:" + QString::number((percent+1)/100.0) + " transparent, stop:1 transparent);"
385         "margin: 2px 0;"
386     "}"
387     "QSlider::handle:horizontal {"
388         "background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);"
389         "border: 1px solid #5c5c5c;"
390         "width: 16px;"
391         "height: 16px;"
392         "margin: -2px 0;"
393         "border-radius: 8px;"
394     "}"
395
396     );
397 }
398
399 */
400
401 void MediaView::downloadStatusChanged() {
402     switch(downloadItem->status()) {
403     case Downloading:
404         startPlaying();
405         break;
406     case Starting:
407         // qDebug() << "Starting";
408         break;
409     case Finished:
410         // qDebug() << "Finished" << mediaObject->state();
411         // if (mediaObject->state() == Phonon::StoppedState) startPlaying();
412         break;
413     case Failed:
414         // qDebug() << "Failed";
415     case Idle:
416         // qDebug() << "Idle";
417         break;
418     }
419 }
420
421 void MediaView::startPlaying() {
422     if (reallyStopped) return;
423     if (!downloadItem) {
424         skip();
425         return;
426     }
427
428     // go!
429     QString source = downloadItem->currentFilename();
430     qDebug() << "Playing" << source;
431     mediaObject->setCurrentSource(source);
432     mediaObject->play();
433
434     // ensure we always have 10 videos ahead
435     listModel->searchNeeded();
436
437     // ensure active item is visible
438     int row = listModel->activeRow();
439     if (row != -1) {
440         QModelIndex index = listModel->index(row, 0, QModelIndex());
441         listView->scrollTo(index, QAbstractItemView::EnsureVisible);
442     }
443
444 #ifdef APP_DEMO
445     demoTimer->start(60000);
446 #endif
447
448 }
449
450 void MediaView::itemActivated(const QModelIndex &index) {
451     if (listModel->rowExists(index.row())) {
452
453         // if it's the current video, just rewind and play
454         Video *activeVideo = listModel->activeVideo();
455         Video *video = listModel->videoAt(index.row());
456         if (activeVideo && video && activeVideo == video) {
457             mediaObject->seek(0);
458             mediaObject->play();
459         } else listModel->setActiveRow(index.row());
460
461     // the user doubleclicked on the "Search More" item
462     } else {
463         listModel->searchMore();
464         listView->selectionModel()->clearSelection();
465     }
466 }
467
468 void MediaView::currentSourceChanged(const Phonon::MediaSource /* source */ ) {
469
470 }
471
472 void MediaView::skipVideo() {
473     // skippedVideo is useful for DELAYED skip operations
474     // in order to be sure that we're skipping the video we wanted
475     // and not another one
476     if (skippedVideo) {
477         if (listModel->activeVideo() != skippedVideo) {
478             qDebug() << "Skip of video canceled";
479             return;
480         }
481         int nextRow = listModel->rowForVideo(skippedVideo);
482         nextRow++;
483         if (nextRow == -1) return;
484         listModel->setActiveRow(nextRow);
485     }
486 }
487
488 void MediaView::skip() {
489     int nextRow = listModel->nextRow();
490     if (nextRow == -1) return;
491     listModel->setActiveRow(nextRow);
492 }
493
494 void MediaView::skipBackward() {
495     int prevRow = listModel->previousRow();
496     if (prevRow == -1) return;
497     listModel->setActiveRow(prevRow);
498 }
499
500 void MediaView::playbackFinished() {
501     // qDebug() << "finished" << mediaObject->currentTime() << mediaObject->totalTime();
502     // add 10 secs for imprecise Phonon backends (VLC, Xine)
503     if (mediaObject->currentTime() + 10000 < mediaObject->totalTime()) {
504         // mediaObject->seek(mediaObject->currentTime());
505         QTimer::singleShot(500, this, SLOT(playbackResume()));
506     } else {
507         QAction* stopAfterThisAction = The::globalActions()->value("stopafterthis");
508         if (stopAfterThisAction->isChecked()) {
509             stopAfterThisAction->setChecked(false);
510         } else skip();
511     }
512 }
513
514 void MediaView::playbackResume() {
515     mediaObject->seek(mediaObject->currentTime());
516     mediaObject->play();
517 }
518
519 void MediaView::openWebPage() {
520     Video* video = listModel->activeVideo();
521     if (!video) return;
522     mediaObject->pause();
523     QDesktopServices::openUrl(video->webpage());
524 }
525
526 void MediaView::copyWebPage() {
527     Video* video = listModel->activeVideo();
528     if (!video) return;
529     QString address = video->webpage().toString();
530     QApplication::clipboard()->setText(address);
531     QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
532     QString message = tr("You can now paste the YouTube link into another application");
533     if (mainWindow) mainWindow->statusBar()->showMessage(message);
534 }
535
536 void MediaView::copyVideoLink() {
537     Video* video = listModel->activeVideo();
538     if (!video) return;
539     QApplication::clipboard()->setText(video->getStreamUrl().toEncoded());
540     QString message = tr("You can now paste the video stream URL into another application")
541             + ". " + tr("The link will be valid only for a limited time.");
542     QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
543     if (mainWindow) mainWindow->statusBar()->showMessage(message);
544 }
545
546 void MediaView::removeSelected() {
547     if (!listView->selectionModel()->hasSelection()) return;
548     QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
549     listModel->removeIndexes(indexes);
550 }
551
552 void MediaView::selectVideos(QList<Video*> videos) {
553     foreach (Video *video, videos) {
554         QModelIndex index = listModel->indexForVideo(video);
555         listView->selectionModel()->select(index, QItemSelectionModel::Select);
556         listView->scrollTo(index, QAbstractItemView::EnsureVisible);
557     }
558 }
559
560 void MediaView::selectionChanged(const QItemSelection & /*selected*/, const QItemSelection & /*deselected*/) {
561     const bool gotSelection = listView->selectionModel()->hasSelection();
562     The::globalActions()->value("remove")->setEnabled(gotSelection);
563     The::globalActions()->value("moveUp")->setEnabled(gotSelection);
564     The::globalActions()->value("moveDown")->setEnabled(gotSelection);
565 }
566
567 void MediaView::moveUpSelected() {
568     if (!listView->selectionModel()->hasSelection()) return;
569
570     QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
571     qStableSort(indexes.begin(), indexes.end());
572     listModel->move(indexes, true);
573
574     // set current index after row moves to something more intuitive
575     int row = indexes.first().row();
576     listView->selectionModel()->setCurrentIndex(listModel->index(row>1?row:1), QItemSelectionModel::NoUpdate);
577 }
578
579 void MediaView::moveDownSelected() {
580     if (!listView->selectionModel()->hasSelection()) return;
581
582     QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
583     qStableSort(indexes.begin(), indexes.end(), qGreater<QModelIndex>());
584     listModel->move(indexes, false);
585
586     // set current index after row moves to something more intuitive (respect 1 static item on bottom)
587     int row = indexes.first().row()+1, max = listModel->rowCount() - 2;
588     listView->selectionModel()->setCurrentIndex(listModel->index(row>max?max:row), QItemSelectionModel::NoUpdate);
589 }
590
591 void MediaView::showVideoContextMenu(QPoint point) {
592     The::globalMenus()->value("video")->popup(videoWidget->mapToGlobal(point));
593 }
594
595 void MediaView::searchMostRelevant() {
596     searchParams->setSortBy(SearchParams::SortByRelevance);
597     search(searchParams);
598 }
599
600 void MediaView::searchMostRecent() {
601     searchParams->setSortBy(SearchParams::SortByNewest);
602     search(searchParams);
603 }
604
605 void MediaView::searchMostViewed() {
606     searchParams->setSortBy(SearchParams::SortByViewCount);
607     search(searchParams);
608 }
609
610 void MediaView::setPlaylistVisible(bool visible) {
611     playlistWidget->setVisible(visible);
612 }
613
614 void MediaView::timerPlay() {
615     // Workaround Phonon bug on Mac OSX
616     // qDebug() << mediaObject->currentTime();
617     if (mediaObject->currentTime() <= 0 && mediaObject->state() == Phonon::PlayingState) {
618         // qDebug() << "Mac playback workaround";
619         mediaObject->pause();
620         // QTimer::singleShot(1000, mediaObject, SLOT(play()));
621         mediaObject->play();
622     }
623 }
624
625 void MediaView::saveSplitterState() {
626     QSettings settings;
627     settings.setValue("splitter", splitter->saveState());
628 }
629
630 #ifdef APP_DEMO
631
632 static QPushButton *continueButton;
633
634 void MediaView::demoMessage() {
635     if (mediaObject->state() != Phonon::PlayingState) return;
636     mediaObject->pause();
637
638     QMessageBox msgBox(this);
639     msgBox.setIconPixmap(QPixmap(":/images/app.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
640     msgBox.setText(tr("This is just the demo version of %1.").arg(Constants::NAME));
641     msgBox.setInformativeText(tr("It allows you to test the application and see if it works for you."));
642     msgBox.setModal(true);
643     // make it a "sheet" on the Mac
644     msgBox.setWindowModality(Qt::WindowModal);
645
646     continueButton = msgBox.addButton("5", QMessageBox::RejectRole);
647     continueButton->setEnabled(false);
648     QPushButton *buyButton = msgBox.addButton(tr("Get the full version"), QMessageBox::ActionRole);
649
650     QTimeLine *timeLine = new QTimeLine(6000, this);
651     timeLine->setCurveShape(QTimeLine::LinearCurve);
652     timeLine->setFrameRange(5, 0);
653     connect(timeLine, SIGNAL(frameChanged(int)), SLOT(updateContinueButton(int)));
654     timeLine->start();
655
656     msgBox.exec();
657
658     if (msgBox.clickedButton() == buyButton) {
659         QDesktopServices::openUrl(QUrl(QString(Constants::WEBSITE) + "#download"));
660     } else {
661         mediaObject->play();
662         demoTimer->start(600000);
663     }
664
665     delete timeLine;
666
667 }
668
669 void MediaView::updateContinueButton(int value) {
670     if (value == 0) {
671         continueButton->setText(tr("Continue"));
672         continueButton->setEnabled(true);
673     } else {
674         continueButton->setText(QString::number(value));
675     }
676 }
677
678 #endif
679
680 void MediaView::downloadVideo() {
681     Video* video = listModel->activeVideo();
682     if (!video) return;
683
684     DownloadManager::instance()->addItem(video);
685
686     // TODO animate
687
688     The::globalActions()->value("downloads")->setVisible(true);
689
690     // The::globalActions()->value("download")->setEnabled(DownloadManager::instance()->itemForVideo(video) == 0);
691
692     QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
693     QString message = tr("Downloading %1").arg(video->title());
694     if (mainWindow) mainWindow->statusBar()->showMessage(message);
695 }
696
697 void MediaView::fullscreen() {
698     videoAreaWidget->setParent(0);
699     videoAreaWidget->showFullScreen();
700 }
701
702 /*
703 void MediaView::setSlider(QSlider *slider) {
704     this->slider = slider;
705     // slider->setEnabled(false);
706     slider->setTracking(false);
707     // connect(slider, SIGNAL(valueChanged(int)), SLOT(sliderMoved(int)));
708 }
709
710 void MediaView::sliderMoved(int value) {
711     qDebug() << __func__;
712     int sliderPercent = (value * 100) / (slider->maximum() - slider->minimum());
713     qDebug() << slider->minimum() << value << slider->maximum();
714     if (sliderPercent <= downloadItem->currentPercent()) {
715         qDebug() << sliderPercent << downloadItem->currentPercent();
716         mediaObject->seek(value);
717     } else {
718         seekTo(value);
719     }
720 }
721
722 void MediaView::seekTo(int value) {
723     qDebug() << __func__;
724     mediaObject->pause();
725     workaroundTimer->stop();
726     errorTimer->stop();
727     // mediaObject->clear();
728
729     QString tempDir = QDesktopServices::storageLocation(QDesktopServices::TempLocation);
730     QString tempFile = tempDir + "/minitube" + QString::number(value) + ".mp4";
731     if (!QFile::remove(tempFile)) {
732         qDebug() << "Cannot remove temp file";
733     }
734     Video *videoCopy = downloadItem->getVideo()->clone();
735     QUrl streamUrl = videoCopy->getStreamUrl();
736     streamUrl.addQueryItem("begin", QString::number(value));
737     if (downloadItem) delete downloadItem;
738     downloadItem = new DownloadItem(videoCopy, streamUrl, tempFile, this);
739     connect(downloadItem, SIGNAL(statusChanged()), SLOT(downloadStatusChanged()));
740     // connect(downloadItem, SIGNAL(finished()), SLOT(itemFinished()));
741     downloadItem->start();
742
743     // slider->setMinimum(value);
744
745 }
746
747 */
748
749 void MediaView::findVideoParts() {
750
751     // parts
752     Video* video = listModel->activeVideo();
753     if (!video) return;
754
755     QString query = video->title();
756
757     static QString optionalSpace = "\\s*";
758     static QString staticCounterSeparators = "[\\/\\-]";
759     QString counterSeparators = "( of | " +
760             tr("of", "Used in video parts, as in '2 of 3'") +
761             " |" + staticCounterSeparators + ")";
762
763     // numbers from 1 to 15
764     static QString counterNumber = "([1-9]|1[0-5])";
765
766     // query.remove(QRegExp(counterSeparators + optionalSpace + counterNumber));
767     query.remove(QRegExp(counterNumber + optionalSpace + counterSeparators + optionalSpace + counterNumber));
768     query.remove(wordRE("pr?t\\.?" + optionalSpace + counterNumber));
769     query.remove(wordRE("ep\\.?" + optionalSpace + counterNumber));
770     query.remove(wordRE("part" + optionalSpace + counterNumber));
771     query.remove(wordRE("episode" + optionalSpace + counterNumber));
772     query.remove(wordRE(tr("part", "This is for video parts, as in 'Cool video - part 1'") +
773                         optionalSpace + counterNumber));
774     query.remove(wordRE(tr("episode", "This is for video parts, as in 'Cool series - episode 1'") +
775                         optionalSpace + counterNumber));
776     query.remove(QRegExp("[\\(\\)\\[\\]]"));
777
778 #define NUMBERS "one|two|three|four|five|six|seven|eight|nine|ten"
779
780     QRegExp englishNumberRE = QRegExp(QLatin1String(".*(") + NUMBERS + ").*", Qt::CaseInsensitive);
781     // bool numberAsWords = englishNumberRE.exactMatch(query);
782     query.remove(englishNumberRE);
783
784     QRegExp localizedNumberRE = QRegExp(QLatin1String(".*(") + tr(NUMBERS) + ").*", Qt::CaseInsensitive);
785     // if (!numberAsWords) numberAsWords = localizedNumberRE.exactMatch(query);
786     query.remove(localizedNumberRE);
787
788     SearchParams *searchParams = new SearchParams();
789     searchParams->setTransient(true);
790     searchParams->setKeywords(query);
791     searchParams->setAuthor(video->author());
792
793     /*
794     if (!numberAsWords) {
795         qDebug() << "We don't have number as words";
796         // searchParams->setSortBy(SearchParams::SortByNewest);
797         // TODO searchParams->setReverseOrder(true);
798         // TODO searchParams->setMax(50);
799     }
800     */
801
802     search(searchParams);
803
804 }
805
806 void MediaView::shareViaTwitter() {
807     Video* video = listModel->activeVideo();
808     if (!video) return;
809     QUrl url("https://twitter.com/intent/tweet");
810     url.addQueryItem("via", "minitubeapp");
811     url.addQueryItem("text", video->title());
812     url.addQueryItem("url", video->webpage().toString());
813     QDesktopServices::openUrl(url);
814 }
815
816 void MediaView::shareViaFacebook() {
817     Video* video = listModel->activeVideo();
818     if (!video) return;
819     QUrl url("https://www.facebook.com/sharer.php");
820     url.addQueryItem("t", video->title());
821     url.addQueryItem("u", video->webpage().toString());
822     QDesktopServices::openUrl(url);
823 }
824
825 void MediaView::shareViaEmail() {
826     Video* video = listModel->activeVideo();
827     if (!video) return;
828     QUrl url("mailto:");
829     url.addQueryItem("subject", video->title());
830     QString body = video->title() + "\n" +
831             video->webpage().toString() + "\n\n" +
832             tr("Sent from %1").arg(Constants::NAME) + "\n" +
833             Constants::WEBSITE;
834     url.addQueryItem("body", body);
835     QDesktopServices::openUrl(url);
836 }
837
838 void MediaView::authorPushed(QModelIndex index) {
839     Video* video = listModel->videoAt(index.row());
840     if (!video) return;
841
842     QString channel = video->author();
843     if (channel.isEmpty()) return;
844
845     SearchParams *searchParams = new SearchParams();
846     searchParams->setAuthor(channel);
847     searchParams->setSortBy(SearchParams::SortByNewest);
848
849     // go!
850     search(searchParams);
851 }