]> git.sur5r.net Git - minitube/blobdiff - src/MediaView.cpp
Imported Upstream version 1.7
[minitube] / src / MediaView.cpp
index ca4ff292bcf39a83eb6a9b0e86853846c5229abe..a77fba219122456205572e7d42eb4b3f5746ccec 100644 (file)
@@ -1,4 +1,5 @@
 #include "MediaView.h"
+#include "playlistview.h"
 #include "playlist/PrettyItemDelegate.h"
 #include "networkaccess.h"
 #include "videowidget.h"
@@ -7,15 +8,16 @@
 #include "downloadmanager.h"
 #include "downloaditem.h"
 #include "MainWindow.h"
+#include "temporary.h"
 
 namespace The {
-    NetworkAccess* http();
+NetworkAccess* http();
 }
 
 namespace The {
-    QMap<QString, QAction*>* globalActions();
-    QMap<QString, QMenu*>* globalMenus();
-    QNetworkAccessManager* networkAccessManager();
+QMap<QString, QAction*>* globalActions();
+QMap<QString, QMenu*>* globalMenus();
+QNetworkAccessManager* networkAccessManager();
 }
 
 MediaView::MediaView(QWidget *parent) : QWidget(parent) {
@@ -23,13 +25,13 @@ MediaView::MediaView(QWidget *parent) : QWidget(parent) {
     reallyStopped = false;
     downloadItem = 0;
 
-    QBoxLayout *layout = new QHBoxLayout();
+    QBoxLayout *layout = new QVBoxLayout();
     layout->setMargin(0);
 
     splitter = new MiniSplitter(this);
     splitter->setChildrenCollapsible(false);
 
-    sortBar = new THBlackBar(this);
+    sortBar = new SegmentedControl(this);
     mostRelevantAction = new QAction(tr("Most relevant"), this);
     QKeySequence keySequence(Qt::CTRL + Qt::Key_1);
     mostRelevantAction->setShortcut(keySequence);
@@ -52,7 +54,7 @@ MediaView::MediaView(QWidget *parent) : QWidget(parent) {
     connect(mostViewedAction, SIGNAL(triggered()), this, SLOT(searchMostViewed()), Qt::QueuedConnection);
     sortBar->addAction(mostViewedAction);
 
-    listView = new QListView(this);
+    listView = new PlaylistView(this);
     listView->setItemDelegate(new PrettyItemDelegate(this));
     listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
 
@@ -82,6 +84,8 @@ MediaView::MediaView(QWidget *parent) : QWidget(parent) {
             SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & )),
             this, SLOT(selectionChanged ( const QItemSelection & , const QItemSelection & )));
 
+    connect(listView, SIGNAL(authorPushed(QModelIndex)), SLOT(authorPushed(QModelIndex)));
+
     playlistWidget = new PlaylistWidget(this, sortBar, listView);
 
     splitter->addWidget(playlistWidget);
@@ -89,7 +93,7 @@ MediaView::MediaView(QWidget *parent) : QWidget(parent) {
     videoAreaWidget = new VideoAreaWidget(this);
     videoAreaWidget->setMinimumSize(320,240);
 
-#ifdef APP_MAC
+#ifdef APP_MAC_NO
     // mouse autohide does not work on the Mac (no mouseMoveEvent)
     videoWidget = new Phonon::VideoWidget(this);
 #else
@@ -107,6 +111,9 @@ MediaView::MediaView(QWidget *parent) : QWidget(parent) {
     layout->addWidget(splitter);
     setLayout(layout);
 
+    splitter->setStretchFactor(0, 1);
+    splitter->setStretchFactor(1, 6);
+
     // restore splitter state
     QSettings settings;
     splitter->restoreState(settings.value("splitter").toByteArray());
@@ -124,7 +131,6 @@ MediaView::MediaView(QWidget *parent) : QWidget(parent) {
 #ifdef APP_DEMO
     demoTimer = new QTimer(this);
     demoTimer->setSingleShot(true);
-    demoTimer->setInterval(60000);
     connect(demoTimer, SIGNAL(timeout()), SLOT(demoMessage()));
 #endif
 
@@ -132,9 +138,12 @@ MediaView::MediaView(QWidget *parent) : QWidget(parent) {
 
 void MediaView::initialize() {
     connect(videoAreaWidget, SIGNAL(doubleClicked()), The::globalActions()->value("fullscreen"), SLOT(trigger()));
+
+    /*
     videoAreaWidget->setContextMenuPolicy(Qt::CustomContextMenu);
     connect(videoAreaWidget, SIGNAL(customContextMenuRequested(QPoint)),
             this, SLOT(showVideoContextMenu(QPoint)));
+            */
 }
 
 void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) {
@@ -154,17 +163,9 @@ void MediaView::search(SearchParams *searchParams) {
 #ifdef APP_DEMO
     demoTimer->stop();
 #endif
-
-    videoAreaWidget->clear();
     workaroundTimer->stop();
     errorTimer->stop();
 
-    mediaObject->pause();
-    if (downloadItem) {
-        delete downloadItem;
-        downloadItem = 0;
-    }
-
     this->searchParams = searchParams;
 
     // start serching for videos
@@ -175,31 +176,35 @@ void MediaView::search(SearchParams *searchParams) {
 
     listView->setFocus();
 
-
     QString keyword = searchParams->keywords();
     QString display = keyword;
-    if (keyword.startsWith("http://")) {
+    if (keyword.startsWith("http://") || keyword.startsWith("https://")) {
         int separator = keyword.indexOf("|");
         if (separator > 0 && separator + 1 < keyword.length()) {
             display = keyword.mid(separator+1);
         }
+    }
 
-        // also hide sidebar
-        playlistWidget->hide();
-    } else playlistWidget->show();
-    // tr("You're watching \"%1\"").arg(searchParams->keywords())
+}
 
+void MediaView::appear() {
+    listView->setFocus();
 }
 
 void MediaView::disappear() {
     timerPlayFlag = true;
 }
 
-void MediaView::handleError(QString message) {
+void MediaView::handleError(QString /* message */) {
+
+    QTimer::singleShot(100, this, SLOT(startPlaying()));
+
+    /*
     videoAreaWidget->showError(message);
     skippedVideo = listModel->activeVideo();
     // recover from errors by skipping to the next video
     errorTimer->start(2000);
+    */
 }
 
 void MediaView::stateChanged(Phonon::State newState, Phonon::State /*oldState*/)
@@ -236,25 +241,24 @@ void MediaView::stateChanged(Phonon::State newState, Phonon::State /*oldState*/)
 
         break;
 
-         case Phonon::PausedState:
+    case Phonon::PausedState:
         qDebug("paused");
         break;
 
-         case Phonon::BufferingState:
+    case Phonon::BufferingState:
         qDebug("buffering");
         break;
 
-         case Phonon::LoadingState:
+    case Phonon::LoadingState:
         qDebug("loading");
         break;
 
-         default:
-        ;
     }
 }
 
 void MediaView::pause() {
     // qDebug() << "pause() called" << mediaObject->state();
+
     switch( mediaObject->state() ) {
     case Phonon::PlayingState:
         mediaObject->pause();
@@ -263,6 +267,11 @@ void MediaView::pause() {
         mediaObject->play();
         break;
     }
+
+}
+
+QRegExp MediaView::wordRE(QString s) {
+    return QRegExp("\\W" + s + "\\W?", Qt::CaseInsensitive);
 }
 
 void MediaView::stop() {
@@ -302,9 +311,9 @@ void MediaView::activeRowChanged(int row) {
     // immediately show the loading widget
     videoAreaWidget->showLoading(video);
 
-    connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)));
+    connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)), Qt::UniqueConnection);
     // TODO handle signal in a proper slot and impl item error status
-    connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)));
+    connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)), Qt::UniqueConnection);
 
     video->loadStreamUrl();
 
@@ -315,7 +324,19 @@ void MediaView::activeRowChanged(int row) {
     QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
     if (mainWindow) mainWindow->statusBar()->showMessage(video->title());
 
+
+    // ensure active item is visible
+    // int row = listModel->activeRow();
+    if (row != -1) {
+        QModelIndex index = listModel->index(row, 0, QModelIndex());
+        listView->scrollTo(index, QAbstractItemView::EnsureVisible);
+    }
+
+    // enable/disable actions
     The::globalActions()->value("download")->setEnabled(DownloadManager::instance()->itemForVideo(video) == 0);
+    The::globalActions()->value("skip")->setEnabled(true);
+    The::globalActions()->value("previous")->setEnabled(row > 0);
+    The::globalActions()->value("stopafterthis")->setEnabled(true);
 
     // see you in gotStreamUrl...
 
@@ -331,16 +352,7 @@ void MediaView::gotStreamUrl(QUrl streamUrl) {
     }
     video->disconnect(this);
 
-
-    QString tempDir = QDesktopServices::storageLocation(QDesktopServices::TempLocation);
-#ifdef Q_WS_X11
-    QString tempFile = tempDir + "/minitube-" + getenv("USERNAME") + ".mp4";
-#else
-    QString tempFile = tempDir + "/minitube.mp4";
-#endif
-    if (QFile::exists(tempFile) && !QFile::remove(tempFile)) {
-        qDebug() << "Cannot remove temp file";
-    }
+    QString tempFile = Temporary::filename();
 
     Video *videoCopy = video->clone();
     if (downloadItem) {
@@ -348,12 +360,12 @@ void MediaView::gotStreamUrl(QUrl streamUrl) {
         delete downloadItem;
     }
     downloadItem = new DownloadItem(videoCopy, streamUrl, tempFile, this);
-    connect(downloadItem, SIGNAL(statusChanged()), SLOT(downloadStatusChanged()));
+    connect(downloadItem, SIGNAL(statusChanged()), SLOT(downloadStatusChanged()), Qt::UniqueConnection);
     // connect(downloadItem, SIGNAL(progress(int)), SLOT(downloadProgress(int)));
-    connect(downloadItem, SIGNAL(bufferProgress(int)), loadingWidget, SLOT(bufferStatus(int)));
+    connect(downloadItem, SIGNAL(bufferProgress(int)), loadingWidget, SLOT(bufferStatus(int)), Qt::UniqueConnection);
     // connect(downloadItem, SIGNAL(finished()), SLOT(itemFinished()));
-    connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)));
-    connect(downloadItem, SIGNAL(error(QString)), SLOT(handleError(QString)));
+    connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)), Qt::UniqueConnection);
+    connect(downloadItem, SIGNAL(error(QString)), SLOT(handleError(QString)), Qt::UniqueConnection);
     downloadItem->start();
 
 }
@@ -408,10 +420,15 @@ void MediaView::downloadStatusChanged() {
 
 void MediaView::startPlaying() {
     if (reallyStopped) return;
+    if (!downloadItem) {
+        skip();
+        return;
+    }
 
     // go!
-    qDebug() << "Playing" << downloadItem->currentFilename();
-    mediaObject->setCurrentSource(downloadItem->currentFilename());
+    QString source = downloadItem->currentFilename();
+    qDebug() << "Playing" << source;
+    mediaObject->setCurrentSource(source);
     mediaObject->play();
 
     // ensure we always have 10 videos ahead
@@ -425,16 +442,27 @@ void MediaView::startPlaying() {
     }
 
 #ifdef APP_DEMO
-    demoTimer->start(30000);
+    demoTimer->start(60000);
 #endif
 
 }
 
 void MediaView::itemActivated(const QModelIndex &index) {
-    if (listModel->rowExists(index.row()))
-        listModel->setActiveRow(index.row());
+    if (listModel->rowExists(index.row())) {
+
+        // if it's the current video, just rewind and play
+        Video *activeVideo = listModel->activeVideo();
+        Video *video = listModel->videoAt(index.row());
+        if (activeVideo && video && activeVideo == video) {
+            mediaObject->seek(0);
+            mediaObject->play();
+        } else listModel->setActiveRow(index.row());
+
     // the user doubleclicked on the "Search More" item
-    else listModel->searchMore();
+    } else {
+        listModel->searchMore();
+        listView->selectionModel()->clearSelection();
+    }
 }
 
 void MediaView::currentSourceChanged(const Phonon::MediaSource /* source */ ) {
@@ -463,13 +491,24 @@ void MediaView::skip() {
     listModel->setActiveRow(nextRow);
 }
 
+void MediaView::skipBackward() {
+    int prevRow = listModel->previousRow();
+    if (prevRow == -1) return;
+    listModel->setActiveRow(prevRow);
+}
+
 void MediaView::playbackFinished() {
     // qDebug() << "finished" << mediaObject->currentTime() << mediaObject->totalTime();
     // add 10 secs for imprecise Phonon backends (VLC, Xine)
     if (mediaObject->currentTime() + 10000 < mediaObject->totalTime()) {
         // mediaObject->seek(mediaObject->currentTime());
-        QTimer::singleShot(3000, this, SLOT(playbackResume()));
-    } else skip();
+        QTimer::singleShot(500, this, SLOT(playbackResume()));
+    } else {
+        QAction* stopAfterThisAction = The::globalActions()->value("stopafterthis");
+        if (stopAfterThisAction->isChecked()) {
+            stopAfterThisAction->setChecked(false);
+        } else skip();
+    }
 }
 
 void MediaView::playbackResume() {
@@ -488,7 +527,6 @@ void MediaView::copyWebPage() {
     Video* video = listModel->activeVideo();
     if (!video) return;
     QString address = video->webpage().toString();
-    address.remove("&feature=youtube_gdata");
     QApplication::clipboard()->setText(address);
     QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
     QString message = tr("You can now paste the YouTube link into another application");
@@ -500,7 +538,7 @@ void MediaView::copyVideoLink() {
     if (!video) return;
     QApplication::clipboard()->setText(video->getStreamUrl().toEncoded());
     QString message = tr("You can now paste the video stream URL into another application")
-                      + ". " + tr("The link will be valid only for a limited time.");
+            + ". " + tr("The link will be valid only for a limited time.");
     QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(window());
     if (mainWindow) mainWindow->statusBar()->showMessage(message);
 }
@@ -590,28 +628,53 @@ void MediaView::saveSplitterState() {
 }
 
 #ifdef APP_DEMO
+
+static QPushButton *continueButton;
+
 void MediaView::demoMessage() {
     if (mediaObject->state() != Phonon::PlayingState) return;
     mediaObject->pause();
 
-    QMessageBox msgBox;
+    QMessageBox msgBox(this);
     msgBox.setIconPixmap(QPixmap(":/images/app.png").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
-    msgBox.setText(tr("This is just the demo version of %1.").arg(Constants::APP_NAME));
+    msgBox.setText(tr("This is just the demo version of %1.").arg(Constants::NAME));
     msgBox.setInformativeText(tr("It allows you to test the application and see if it works for you."));
     msgBox.setModal(true);
+    // make it a "sheet" on the Mac
+    msgBox.setWindowModality(Qt::WindowModal);
 
-    QPushButton *quitButton = msgBox.addButton(tr("Continue"), QMessageBox::RejectRole);
+    continueButton = msgBox.addButton("5", QMessageBox::RejectRole);
+    continueButton->setEnabled(false);
     QPushButton *buyButton = msgBox.addButton(tr("Get the full version"), QMessageBox::ActionRole);
 
+    QTimeLine *timeLine = new QTimeLine(6000, this);
+    timeLine->setCurveShape(QTimeLine::LinearCurve);
+    timeLine->setFrameRange(5, 0);
+    connect(timeLine, SIGNAL(frameChanged(int)), SLOT(updateContinueButton(int)));
+    timeLine->start();
+
     msgBox.exec();
 
     if (msgBox.clickedButton() == buyButton) {
-        QDesktopServices::openUrl(QString(Constants::WEBSITE) + "#download");
+        QDesktopServices::openUrl(QUrl(QString(Constants::WEBSITE) + "#download"));
     } else {
         mediaObject->play();
-        demoTimer->start(300000);
+        demoTimer->start(600000);
     }
+
+    delete timeLine;
+
 }
+
+void MediaView::updateContinueButton(int value) {
+    if (value == 0) {
+        continueButton->setText(tr("Continue"));
+        continueButton->setEnabled(true);
+    } else {
+        continueButton->setText(QString::number(value));
+    }
+}
+
 #endif
 
 void MediaView::downloadVideo() {
@@ -682,3 +745,107 @@ void MediaView::seekTo(int value) {
 }
 
 */
+
+void MediaView::findVideoParts() {
+
+    // parts
+    Video* video = listModel->activeVideo();
+    if (!video) return;
+
+    QString query = video->title();
+
+    static QString optionalSpace = "\\s*";
+    static QString staticCounterSeparators = "[\\/\\-]";
+    QString counterSeparators = "( of | " +
+            tr("of", "Used in video parts, as in '2 of 3'") +
+            " |" + staticCounterSeparators + ")";
+
+    // numbers from 1 to 15
+    static QString counterNumber = "([1-9]|1[0-5])";
+
+    // query.remove(QRegExp(counterSeparators + optionalSpace + counterNumber));
+    query.remove(QRegExp(counterNumber + optionalSpace + counterSeparators + optionalSpace + counterNumber));
+    query.remove(wordRE("pr?t\\.?" + optionalSpace + counterNumber));
+    query.remove(wordRE("ep\\.?" + optionalSpace + counterNumber));
+    query.remove(wordRE("part" + optionalSpace + counterNumber));
+    query.remove(wordRE("episode" + optionalSpace + counterNumber));
+    query.remove(wordRE(tr("part", "This is for video parts, as in 'Cool video - part 1'") +
+                        optionalSpace + counterNumber));
+    query.remove(wordRE(tr("episode", "This is for video parts, as in 'Cool series - episode 1'") +
+                        optionalSpace + counterNumber));
+    query.remove(QRegExp("[\\(\\)\\[\\]]"));
+
+#define NUMBERS "one|two|three|four|five|six|seven|eight|nine|ten"
+
+    QRegExp englishNumberRE = QRegExp(QLatin1String(".*(") + NUMBERS + ").*", Qt::CaseInsensitive);
+    // bool numberAsWords = englishNumberRE.exactMatch(query);
+    query.remove(englishNumberRE);
+
+    QRegExp localizedNumberRE = QRegExp(QLatin1String(".*(") + tr(NUMBERS) + ").*", Qt::CaseInsensitive);
+    // if (!numberAsWords) numberAsWords = localizedNumberRE.exactMatch(query);
+    query.remove(localizedNumberRE);
+
+    SearchParams *searchParams = new SearchParams();
+    searchParams->setTransient(true);
+    searchParams->setKeywords(query);
+    searchParams->setAuthor(video->author());
+
+    /*
+    if (!numberAsWords) {
+        qDebug() << "We don't have number as words";
+        // searchParams->setSortBy(SearchParams::SortByNewest);
+        // TODO searchParams->setReverseOrder(true);
+        // TODO searchParams->setMax(50);
+    }
+    */
+
+    search(searchParams);
+
+}
+
+void MediaView::shareViaTwitter() {
+    Video* video = listModel->activeVideo();
+    if (!video) return;
+    QUrl url("https://twitter.com/intent/tweet");
+    url.addQueryItem("via", "minitubeapp");
+    url.addQueryItem("text", video->title());
+    url.addQueryItem("url", video->webpage().toString());
+    QDesktopServices::openUrl(url);
+}
+
+void MediaView::shareViaFacebook() {
+    Video* video = listModel->activeVideo();
+    if (!video) return;
+    QUrl url("https://www.facebook.com/sharer.php");
+    url.addQueryItem("t", video->title());
+    url.addQueryItem("u", video->webpage().toString());
+    QDesktopServices::openUrl(url);
+}
+
+void MediaView::shareViaEmail() {
+    Video* video = listModel->activeVideo();
+    if (!video) return;
+    QUrl url("mailto:");
+    url.addQueryItem("subject", video->title());
+    QString body = video->title() + "\n" +
+            video->webpage().toString() + "\n\n" +
+            tr("Sent from %1").arg(Constants::NAME) + "\n" +
+            Constants::WEBSITE;
+    url.addQueryItem("body", body);
+    QDesktopServices::openUrl(url);
+}
+
+void MediaView::authorPushed(QModelIndex index) {
+    Video* video = listModel->videoAt(index.row());
+    if (!video) return;
+
+    QString channel = video->author();
+    if (channel.isEmpty()) return;
+
+    SearchParams *searchParams = new SearchParams();
+    searchParams->setAuthor(channel);
+    searchParams->setSortBy(SearchParams::SortByNewest);
+
+    // go!
+    search(searchParams);
+}