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