X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2FMediaView.cpp;h=a77fba219122456205572e7d42eb4b3f5746ccec;hb=3c8c537559e6bef5f019196b0989e95863ddd3ee;hp=8890acebddf2abeee5818edca4debedc2999cd14;hpb=e536746e91459fb3a7735f36caee77f299c765e2;p=minitube diff --git a/src/MediaView.cpp b/src/MediaView.cpp index 8890ace..a77fba2 100644 --- a/src/MediaView.cpp +++ b/src/MediaView.cpp @@ -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* globalActions(); - QMap* globalMenus(); - QNetworkAccessManager* networkAccessManager(); +QMap* globalActions(); +QMap* 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,20 +138,23 @@ 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) { this->mediaObject = mediaObject; Phonon::createPath(this->mediaObject, videoWidget); - connect(mediaObject, SIGNAL(finished()), this, SLOT(skip())); + connect(mediaObject, SIGNAL(finished()), this, SLOT(playbackFinished())); connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged(Phonon::State, Phonon::State))); connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)), this, SLOT(currentSourceChanged(Phonon::MediaSource))); - connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int))); + // connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int))); } void MediaView::search(SearchParams *searchParams) { @@ -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,28 +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); } } - // 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*/) @@ -214,44 +222,43 @@ void MediaView::stateChanged(Phonon::State newState, Phonon::State /*oldState*/) break; case Phonon::PlayingState: - //qDebug("playing"); + // qDebug("playing"); videoAreaWidget->showVideo(); break; case Phonon::StoppedState: - //qDebug("stopped"); + // qDebug("stopped"); // play() has already been called when setting the source // but Phonon on Linux needs a little more help to start playback - if (!reallyStopped) mediaObject->play(); + // if (!reallyStopped) mediaObject->play(); #ifdef APP_MAC // Workaround for Mac playback start problem if (!timerPlayFlag) { - workaroundTimer->start(); + // workaroundTimer->start(); } #endif break; - case Phonon::PausedState: - //qDebug("paused"); + case Phonon::PausedState: + qDebug("paused"); break; - case Phonon::BufferingState: - //qDebug("buffering"); + case Phonon::BufferingState: + qDebug("buffering"); break; - case Phonon::LoadingState: - //qDebug("loading"); + case Phonon::LoadingState: + qDebug("loading"); break; - default: - ; } } void MediaView::pause() { // qDebug() << "pause() called" << mediaObject->state(); + switch( mediaObject->state() ) { case Phonon::PlayingState: mediaObject->pause(); @@ -260,6 +267,11 @@ void MediaView::pause() { mediaObject->play(); break; } + +} + +QRegExp MediaView::wordRE(QString s) { + return QRegExp("\\W" + s + "\\W?", Qt::CaseInsensitive); } void MediaView::stop() { @@ -271,6 +283,7 @@ void MediaView::stop() { errorTimer->stop(); listView->selectionModel()->clearSelection(); if (downloadItem) { + downloadItem->stop(); delete downloadItem; downloadItem = 0; } @@ -289,6 +302,7 @@ void MediaView::activeRowChanged(int row) { mediaObject->pause(); if (downloadItem) { + downloadItem->stop(); delete downloadItem; downloadItem = 0; } @@ -297,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(); @@ -310,7 +324,19 @@ void MediaView::activeRowChanged(int row) { QMainWindow* mainWindow = dynamic_cast(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... @@ -326,18 +352,20 @@ void MediaView::gotStreamUrl(QUrl streamUrl) { } video->disconnect(this); - QString tempDir = QDesktopServices::storageLocation(QDesktopServices::TempLocation); - QString tempFile = tempDir + "/minitube.mp4"; - if (!QFile::remove(tempFile)) { - qDebug() << "Cannot remove temp file"; - } + QString tempFile = Temporary::filename(); Video *videoCopy = video->clone(); - if (downloadItem) delete downloadItem; + if (downloadItem) { + downloadItem->stop(); + 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)), Qt::UniqueConnection); // connect(downloadItem, SIGNAL(finished()), SLOT(itemFinished())); + connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)), Qt::UniqueConnection); + connect(downloadItem, SIGNAL(error(QString)), SLOT(handleError(QString)), Qt::UniqueConnection); downloadItem->start(); } @@ -376,25 +404,31 @@ void MediaView::downloadStatusChanged() { startPlaying(); break; case Starting: - qDebug() << "Starting"; + // qDebug() << "Starting"; break; case Finished: - qDebug() << "Finished"; + // qDebug() << "Finished" << mediaObject->state(); + // if (mediaObject->state() == Phonon::StoppedState) startPlaying(); break; case Failed: - qDebug() << "Failed"; + // qDebug() << "Failed"; case Idle: - qDebug() << "Idle"; + // qDebug() << "Idle"; break; } } 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 @@ -408,16 +442,27 @@ void MediaView::startPlaying() { } #ifdef APP_DEMO - demoTimer->start(); + 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 */ ) { @@ -446,6 +491,31 @@ 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(500, this, SLOT(playbackResume())); + } else { + QAction* stopAfterThisAction = The::globalActions()->value("stopafterthis"); + if (stopAfterThisAction->isChecked()) { + stopAfterThisAction->setChecked(false); + } else skip(); + } +} + +void MediaView::playbackResume() { + mediaObject->seek(mediaObject->currentTime()); + mediaObject->play(); +} + void MediaView::openWebPage() { Video* video = listModel->activeVideo(); if (!video) return; @@ -457,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(window()); QString message = tr("You can now paste the YouTube link into another application"); @@ -469,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(window()); if (mainWindow) mainWindow->statusBar()->showMessage(message); } @@ -559,27 +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(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() { @@ -650,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); +}