From 5a90b894c888391012bcf00b34bad482adaa85af Mon Sep 17 00:00:00 2001 From: Flavio Date: Fri, 6 Sep 2013 16:22:58 +0200 Subject: [PATCH] Seeking --- minitube.pro | 8 +- src/downloaditem.cpp | 90 ++++++++++++++++--- src/downloaditem.h | 9 ++ src/mainwindow.cpp | 27 ++++-- src/mainwindow.h | 6 ++ src/mediaview.cpp | 201 ++++++++++++++++++++---------------------- src/mediaview.h | 10 +-- src/networkaccess.cpp | 7 +- src/networkaccess.h | 2 +- src/seekslider.cpp | 15 ++++ src/seekslider.h | 15 ++++ 11 files changed, 253 insertions(+), 137 deletions(-) create mode 100644 src/seekslider.cpp create mode 100644 src/seekslider.h diff --git a/minitube.pro b/minitube.pro index 532c28a..40fb16d 100644 --- a/minitube.pro +++ b/minitube.pro @@ -1,6 +1,6 @@ CONFIG += release TEMPLATE = app -VERSION = 2.1.2 +VERSION = 2.1.3 DEFINES += APP_VERSION="$$VERSION" APP_NAME = Minitube @@ -80,7 +80,8 @@ HEADERS += \ src/aggregatevideosource.h \ src/channelview.h \ src/channelitemdelegate.h \ - src/jsfunctions.h + src/jsfunctions.h \ + src/seekslider.h SOURCES += src/main.cpp \ src/searchlineedit.cpp \ src/urllineedit.cpp \ @@ -142,7 +143,8 @@ SOURCES += src/main.cpp \ src/aggregatevideosource.cpp \ src/channelview.cpp \ src/channelitemdelegate.cpp \ - src/jsfunctions.cpp + src/jsfunctions.cpp \ + src/seekslider.cpp RESOURCES += resources.qrc DESTDIR = build/target/ OBJECTS_DIR = build/obj/ diff --git a/src/downloaditem.cpp b/src/downloaditem.cpp index 12bb7c2..9b79821 100644 --- a/src/downloaditem.cpp +++ b/src/downloaditem.cpp @@ -39,6 +39,8 @@ DownloadItem::DownloadItem(Video *video, QUrl url, QString filename, QObject *pa , m_startedSaving(false) , m_finishedDownloading(false) , m_url(url) + , m_offset(0) + , sendStatusChanges(true) , m_file(filename) , m_reply(0) , video(video) @@ -48,6 +50,9 @@ DownloadItem::DownloadItem(Video *video, QUrl url, QString filename, QObject *pa speedCheckTimer->setInterval(2000); speedCheckTimer->setSingleShot(true); connect(speedCheckTimer, SIGNAL(timeout()), SLOT(speedCheck())); + + if (m_file.exists()) + m_file.remove(); } DownloadItem::~DownloadItem() { @@ -61,8 +66,70 @@ DownloadItem::~DownloadItem() { } } +bool DownloadItem::needsDownload(qint64 offset) { + return offset < m_offset || offset > m_offset + m_bytesReceived; +} + +bool DownloadItem::isBuffered(qint64 offset) { + QMap::iterator i; + for (i = buffers.begin(); i != buffers.end(); ++i) { + if (offset >= i.key() && offset <= i.value()) { + // qDebug() << "Buffered! " << i.key() << ":" << i.value(); + return true; + } + } + // qDebug() << offset << "is not buffered"; + return false; +} + +qint64 DownloadItem::blankAtOffset(qint64 offset) { + // qDebug() << buffers; + QMap::iterator i; + for (i = buffers.begin(); i != buffers.end(); ++i) { + if (offset >= i.key() && offset <= i.value()) { + // qDebug() << "Offset was" << offset << "now" << i.value(); + return i.value() + 1; + } + } + return offset; +} + +void DownloadItem::seekTo(qint64 offset, bool sendStatusChanges) { + // qDebug() << __PRETTY_FUNCTION__ << offset << sendStatusChanges; + stop(); + if (m_bytesReceived > 0) { + bool bufferModified = false; + QMap::iterator i; + for (i = buffers.begin(); i != buffers.end(); ++i) { + if (m_offset - 1 <= i.value()) { + /* + qDebug() << "Extending existing buffer " + << i.key() << i.value() << "now" << m_offset + m_bytesReceived; + */ + bufferModified = true; + i.value() = m_offset + m_bytesReceived; + break; + } + } + if (!bufferModified) + buffers.insert(m_offset, m_offset + m_bytesReceived); + } + m_offset = offset; + this->sendStatusChanges = sendStatusChanges; + bool seekSuccess = m_file.seek(offset); + if (!seekSuccess) { + qWarning() << "Cannot seek to offset" << offset << "in file" << m_file.fileName(); + return; + } + start(); +} + void DownloadItem::start() { - m_reply = The::http()->request(m_url); + // qDebug() << "Starting download at" << m_offset; + if (m_offset > 0) + m_reply = The::http()->request(m_url, QNetworkAccessManager::GetOperation, QByteArray(), m_offset); + else + m_reply = The::http()->request(m_url); init(); } @@ -70,11 +137,8 @@ void DownloadItem::init() { if (!m_reply) return; - if (m_file.exists()) - m_file.remove(); - m_status = Starting; - + m_bytesReceived = 0; m_startedSaving = false; m_finishedDownloading = false; @@ -147,6 +211,7 @@ void DownloadItem::downloadReadyRead() { emit statusChanged(); } + // qWarning() << __PRETTY_FUNCTION__ << m_file.pos(); if (-1 == m_file.write(m_reply->readAll())) { qWarning() << "Error saving." << m_file.errorString(); } else { @@ -183,10 +248,7 @@ void DownloadItem::metaDataChanged() { tryAgain(); return; } - -#ifdef DOWNLOADMANAGER_DEBUG - qDebug() << "DownloadItem::" << __FUNCTION__ << "not handled."; -#endif + // qDebug() << m_reply->rawHeaderList(); } int DownloadItem::initialBufferSize() { @@ -204,13 +266,15 @@ int DownloadItem::initialBufferSize() { void DownloadItem::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - // qDebug() << bytesReceived << bytesTotal << m_downloadTime.elapsed(); + // qDebug() << __PRETTY_FUNCTION__ << bytesReceived << bytesTotal << m_downloadTime.elapsed(); + + m_bytesReceived = bytesReceived; + + if (!sendStatusChanges) return; if (m_lastProgressTime.elapsed() < 150) return; m_lastProgressTime.start(); - m_bytesReceived = bytesReceived; - if (m_status != Downloading) { int neededBytes = (int) (bytesTotal * .005); @@ -327,7 +391,7 @@ void DownloadItem::requestFinished() { m_status = Downloading; emit statusChanged(); } - m_file.close(); + if (m_offset == 0) m_file.close(); m_status = Finished; m_totalTime = m_downloadTime.elapsed() / 1000.0; emit statusChanged(); diff --git a/src/downloaditem.h b/src/downloaditem.h index 7a100e0..2c074f0 100644 --- a/src/downloaditem.h +++ b/src/downloaditem.h @@ -61,6 +61,11 @@ public: static QString formattedSpeed(double speed); static QString formattedTime(double time, bool remaining = true); QString errorMessage() const; + qint64 offset() const { return m_offset; } + bool needsDownload(qint64 offset); + bool isBuffered(qint64 offset); + qint64 blankAtOffset(qint64 offset); + void seekTo(qint64 offset, bool sendStatusChanges = true); public slots: void start(); @@ -92,6 +97,9 @@ private: QUrl m_url; + qint64 m_offset; + bool sendStatusChanges; + QFile m_file; QNetworkReply *m_reply; Video *video; @@ -101,6 +109,7 @@ private: QTimer *speedCheckTimer; + QMap buffers; }; // This is required in order to use QPointer as a QVariant diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3ae36bd..3392ff2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -69,6 +69,7 @@ $END_LICENSE */ #include "database.h" #include "videoareawidget.h" #include "jsfunctions.h" +#include "seekslider.h" static MainWindow *singleton = 0; @@ -565,6 +566,11 @@ void MainWindow::createActions() { connect(action, SIGNAL(triggered()), mediaView, SLOT(relatedVideos())); actions->insert("related-videos", action); + action = new QAction(tr("Open in &Browser..."), this); + action->setEnabled(false); + actions->insert("open-in-browser", action); + connect(action, SIGNAL(triggered()), mediaView, SLOT(openInBrowser())); + #ifdef APP_ACTIVATION Extra::createActivationAction(tr("Buy %1...").arg(Constants::NAME)); #endif @@ -630,6 +636,7 @@ void MainWindow::createMenus() { videoMenu->addSeparator(); videoMenu->addAction(The::globalActions()->value("download")); videoMenu->addAction(copyLinkAct); + videoMenu->addAction(The::globalActions()->value("open-in-browser")); // videoMenu->addAction(The::globalActions()->value("snapshot")); QMenu* viewMenu = menuBar()->addMenu(tr("&View")); @@ -695,20 +702,23 @@ void MainWindow::createToolBars() { currentTime->setFont(smallerFont); mainToolBar->addWidget(currentTime); +#ifdef APP_PHONON_SEEK mainToolBar->addWidget(new Spacer()); - seekSlider = new Phonon::SeekSlider(this); + seekSlider->setVisible(false); seekSlider->setIconVisible(false); seekSlider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); mainToolBar->addWidget(seekSlider); +#endif - /* mainToolBar->addWidget(new Spacer()); - slider = new QSlider(this); + slider = new SeekSlider(this); + slider->setEnabled(false); + slider->setTracking(false); + slider->setMaximum(1000); slider->setOrientation(Qt::Horizontal); slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); mainToolBar->addWidget(slider); -*/ mainToolBar->addWidget(new Spacer()); @@ -1266,7 +1276,9 @@ void MainWindow::initPhonon() { this, SLOT(stateChanged(Phonon::State, Phonon::State))); connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64))); connect(mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); +#ifdef APP_PHONON_SEEK seekSlider->setMediaObject(mediaObject); +#endif audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this); connect(audioOutput, SIGNAL(volumeChanged(qreal)), this, SLOT(volumeChanged(qreal))); connect(audioOutput, SIGNAL(mutedChanged(bool)), this, SLOT(volumeMutedChanged(bool))); @@ -1291,11 +1303,12 @@ void MainWindow::tick(qint64 time) { const qint64 remainingTime = mediaObject->remainingTime(); currentTime->setStatusTip(tr("Remaining time: %1").arg(formatTime(remainingTime))); - /* slider->blockSignals(true); - slider->setValue(time/1000); + const qint64 totalTime = mediaObject->totalTime(); + // qWarning() << totalTime << time << time * 100 / totalTime; + if (totalTime > 0 && time > 0 && !slider->isSliderDown() && mediaObject->state() == Phonon::PlayingState) + slider->setValue(time * slider->maximum() / totalTime); slider->blockSignals(false); - */ } void MainWindow::totalTimeChanged(qint64 time) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 73f09b5..404fa13 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -44,7 +44,10 @@ public: static MainWindow* instance(); MainWindow(); ~MainWindow(); +#ifdef APP_PHONON_SEEK Phonon::SeekSlider* getSeekSlider() { return seekSlider; } +#endif + QSlider* getSlider() { return slider; } void readSettings(); void writeSettings(); static void printHelp(); @@ -192,7 +195,10 @@ private: QAction *regionAction; // phonon + QSlider *slider; +#ifdef APP_PHONON_SEEK Phonon::SeekSlider *seekSlider; +#endif Phonon::VolumeSlider *volumeSlider; Phonon::MediaObject *mediaObject; Phonon::AudioOutput *audioOutput; diff --git a/src/mediaview.cpp b/src/mediaview.cpp index 9f5aafa..c1a0846 100644 --- a/src/mediaview.cpp +++ b/src/mediaview.cpp @@ -142,10 +142,10 @@ void MediaView::initialize() { << The::globalActions()->value("webpage") << The::globalActions()->value("pagelink") << The::globalActions()->value("videolink") + << The::globalActions()->value("open-in-browser") << The::globalActions()->value("findVideoParts") << The::globalActions()->value("skip") << The::globalActions()->value("previous") - // << The::globalActions()->value("download") << The::globalActions()->value("stopafterthis") << The::globalActions()->value("related-videos") << The::globalActions()->value("refine-search") @@ -153,6 +153,9 @@ void MediaView::initialize() { << The::globalActions()->value("facebook") << The::globalActions()->value("buffer") << The::globalActions()->value("email"); + + QSlider *slider = MainWindow::instance()->getSlider(); + connect(slider, SIGNAL(valueChanged(int)), SLOT(sliderMoved(int))); } void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) { @@ -161,17 +164,6 @@ void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) { connect(mediaObject, SIGNAL(finished()), SLOT(playbackFinished())); connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)), SLOT(stateChanged(Phonon::State, Phonon::State))); - /* - const char* s = Constants::NAME; - const int l = strlen(s); - int t = The::globalActions()->count(); - for (int i = 0; i < l; i++) { - t += s[i]; - qDebug() << t << The::globalActions()->count(); - } - qDebug() << t << The::globalActions()->count(); - if (t != s[0]) return; - */ connect(mediaObject, SIGNAL(aboutToFinish()), SLOT(aboutToFinish())); } @@ -332,6 +324,7 @@ void MediaView::stop() { downloadItem->stop(); delete downloadItem; downloadItem = 0; + currentVideoSize = 0; } The::globalActions()->value("refine-search")->setChecked(false); updateSubscriptionAction(0, false); @@ -348,6 +341,10 @@ void MediaView::stop() { mediaObject->stop(); currentVideoId.clear(); + + QSlider *slider = MainWindow::instance()->getSlider(); + slider->setEnabled(false); + slider->setValue(0); } const QString & MediaView::getCurrentVideoId() { @@ -364,6 +361,7 @@ void MediaView::activeRowChanged(int row) { downloadItem->stop(); delete downloadItem; downloadItem = 0; + currentVideoSize = 0; } Video *video = playlistModel->videoAt(row); @@ -390,7 +388,6 @@ void MediaView::activeRowChanged(int row) { // 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); The::globalActions()->value("related-videos")->setEnabled(true); @@ -413,6 +410,10 @@ void MediaView::activeRowChanged(int row) { foreach (QAction *action, currentVideoActions) action->setEnabled(true); + QSlider *slider = MainWindow::instance()->getSlider(); + slider->setEnabled(false); + slider->setValue(0); + // see you in gotStreamUrl... } @@ -436,24 +437,7 @@ void MediaView::gotStreamUrl(QUrl streamUrl) { mediaObject->setCurrentSource(streamUrl); mediaObject->play(); #else - QString tempFile = Temporary::filename(); - Video *videoCopy = video->clone(); - if (downloadItem) { - downloadItem->stop(); - delete downloadItem; - } - downloadItem = new DownloadItem(videoCopy, streamUrl, tempFile, this); - 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(); + startDownloading(); #endif // ensure we always have 10 videos ahead @@ -478,50 +462,31 @@ void MediaView::gotStreamUrl(QUrl streamUrl) { ChannelAggregator::instance()->videoWatched(video); } -/* -void MediaView::downloadProgress(int percent) { - MainWindow* mainWindow = dynamic_cast(window()); - - mainWindow->getSeekSlider()->setStyleSheet(" QSlider::groove:horizontal {" - "border: 1px solid #999999;" - // "border-left: 50px solid rgba(255, 0, 0, 128);" - "height: 8px;" - "background: qlineargradient(x1:0, y1:0, x2:.5, y2:0, stop:0 rgba(255, 0, 0, 92), stop:" - + QString::number(percent/100.0) + - - " rgba(255, 0, 0, 92), stop:" + QString::number((percent+1)/100.0) + " transparent, stop:1 transparent);" - "margin: 2px 0;" - "}" - "QSlider::handle:horizontal {" - "background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);" - "border: 1px solid #5c5c5c;" - "width: 16px;" - "height: 16px;" - "margin: -2px 0;" - "border-radius: 8px;" - "}" - - ); -} -*/ - void MediaView::downloadStatusChanged() { + // qDebug() << __PRETTY_FUNCTION__; switch(downloadItem->status()) { case Downloading: - startPlaying(); + // qDebug() << "Downloading"; + if (downloadItem->offset() == 0) startPlaying(); + else { + // qDebug() << "Seeking to" << downloadItem->offset(); + mediaObject->seek(offsetToTime(downloadItem->offset())); + mediaObject->play(); + } break; case Starting: // qDebug() << "Starting"; break; case Finished: // qDebug() << "Finished" << mediaObject->state(); - // if (mediaObject->state() == Phonon::StoppedState) startPlaying(); #ifdef Q_WS_X11 - MainWindow::instance()->getSeekSlider()->setEnabled(mediaObject->isSeekable()); + // MainWindow::instance()->getSeekSlider()->setEnabled(mediaObject->isSeekable()); #endif break; case Failed: // qDebug() << "Failed"; + skip(); + break; case Idle: // qDebug() << "Idle"; break; @@ -529,21 +494,29 @@ void MediaView::downloadStatusChanged() { } void MediaView::startPlaying() { + // qDebug() << __PRETTY_FUNCTION__; if (stopped) return; if (!downloadItem) { skip(); return; } + if (downloadItem->offset() == 0) { + currentVideoSize = downloadItem->bytesTotal(); + // qDebug() << "currentVideoSize" << currentVideoSize; + } + // go! QString source = downloadItem->currentFilename(); - // qDebug() << "Playing" << source; + qDebug() << "Playing" << source << QFile::exists(source); mediaObject->setCurrentSource(source); mediaObject->play(); #ifdef Q_WS_X11 - MainWindow::instance()->getSeekSlider()->setEnabled(false); + // MainWindow::instance()->getSeekSlider()->setEnabled(false); #endif + QSlider *slider = MainWindow::instance()->getSlider(); + slider->setEnabled(true); } void MediaView::itemActivated(const QModelIndex &index) { @@ -553,7 +526,8 @@ void MediaView::itemActivated(const QModelIndex &index) { Video *activeVideo = playlistModel->activeVideo(); Video *video = playlistModel->videoAt(index.row()); if (activeVideo && video && activeVideo == video) { - mediaObject->seek(0); + // mediaObject->seek(0); + sliderMoved(0); mediaObject->play(); } else playlistModel->setActiveRow(index.row()); @@ -597,7 +571,6 @@ void MediaView::aboutToFinish() { qint64 totalTime = mediaObject->totalTime(); qDebug() << __PRETTY_FUNCTION__ << currentTime << totalTime; if (totalTime < 1 || currentTime + 10000 < totalTime) { - // mediaObject->seek(mediaObject->currentTime()); // QTimer::singleShot(500, this, SLOT(playbackResume())); mediaObject->seek(currentTime); mediaObject->play(); @@ -606,8 +579,8 @@ void MediaView::aboutToFinish() { void MediaView::playbackFinished() { if (stopped) return; - const int totalTime = mediaObject->totalTime(); - const int currentTime = mediaObject->currentTime(); + const qint64 totalTime = mediaObject->totalTime(); + const qint64 currentTime = mediaObject->currentTime(); qDebug() << __PRETTY_FUNCTION__ << mediaObject->currentTime() << totalTime; // add 10 secs for imprecise Phonon backends (VLC, Xine) if (totalTime < 1 || (currentTime > 0 && currentTime + 10000 < totalTime)) { @@ -623,8 +596,10 @@ void MediaView::playbackFinished() { void MediaView::playbackResume() { if (stopped) return; - qDebug() << __PRETTY_FUNCTION__ << mediaObject->currentTime(); - mediaObject->seek(mediaObject->currentTime()); + const qint64 currentTime = mediaObject->currentTime(); + qDebug() << __PRETTY_FUNCTION__ << currentTime; + if (currentTime > 0) + mediaObject->seek(currentTime); mediaObject->play(); } @@ -653,6 +628,13 @@ void MediaView::copyVideoLink() { MainWindow::instance()->showMessage(message); } +void MediaView::openInBrowser() { + Video* video = playlistModel->activeVideo(); + if (!video) return; + mediaObject->pause(); + QDesktopServices::openUrl(video->getStreamUrl()); +} + void MediaView::removeSelected() { if (!playlistView->selectionModel()->hasSelection()) return; QModelIndexList indexes = playlistView->selectionModel()->selectedIndexes(); @@ -792,52 +774,59 @@ void MediaView::fullscreen() { videoAreaWidget->showFullScreen(); } -/* -void MediaView::setSlider(QSlider *slider) { - this->slider = slider; - // slider->setEnabled(false); - slider->setTracking(false); - // connect(slider, SIGNAL(valueChanged(int)), SLOT(sliderMoved(int))); +void MediaView::startDownloading() { + Video *video = playlistModel->activeVideo(); + if (!video) return; + Video *videoCopy = video->clone(); + if (downloadItem) { + downloadItem->stop(); + delete downloadItem; + } + QString tempFile = Temporary::filename(); + downloadItem = new DownloadItem(videoCopy, video->getStreamUrl(), tempFile, this); + connect(downloadItem, SIGNAL(statusChanged()), + SLOT(downloadStatusChanged()), Qt::UniqueConnection); + 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(); } void MediaView::sliderMoved(int value) { - qDebug() << __func__; - int sliderPercent = (value * 100) / (slider->maximum() - slider->minimum()); - qDebug() << slider->minimum() << value << slider->maximum(); - if (sliderPercent <= downloadItem->currentPercent()) { - qDebug() << sliderPercent << downloadItem->currentPercent(); - mediaObject->seek(value); + if (currentVideoSize <= 0 || !downloadItem || !mediaObject->isSeekable()) + return; + + QSlider *slider = MainWindow::instance()->getSlider(); + if (slider->isSliderDown()) return; + + qint64 offset = (currentVideoSize * value) / slider->maximum(); + + bool needsDownload = downloadItem->needsDownload(offset); + if (needsDownload) { + if (downloadItem->isBuffered(offset)) { + qint64 realOffset = downloadItem->blankAtOffset(offset); + if (offset < currentVideoSize) + downloadItem->seekTo(realOffset, false); + mediaObject->seek(offsetToTime(offset)); + } else { + mediaObject->pause(); + downloadItem->seekTo(offset); + } } else { - seekTo(value); + // qDebug() << "simple seek"; + mediaObject->seek(offsetToTime(offset)); } } -void MediaView::seekTo(int value) { - qDebug() << __func__; - mediaObject->pause(); - errorTimer->stop(); - // mediaObject->clear(); - - QString tempDir = QDesktopServices::storageLocation(QDesktopServices::TempLocation); - QString tempFile = tempDir + "/minitube" + QString::number(value) + ".mp4"; - if (!QFile::remove(tempFile)) { - qDebug() << "Cannot remove temp file"; - } - Video *videoCopy = downloadItem->getVideo()->clone(); - QUrl streamUrl = videoCopy->getStreamUrl(); - streamUrl.addQueryItem("begin", QString::number(value)); - if (downloadItem) delete downloadItem; - downloadItem = new DownloadItem(videoCopy, streamUrl, tempFile, this); - connect(downloadItem, SIGNAL(statusChanged()), SLOT(downloadStatusChanged())); - // connect(downloadItem, SIGNAL(finished()), SLOT(itemFinished())); - downloadItem->start(); - - // slider->setMinimum(value); - +qint64 MediaView::offsetToTime(qint64 offset) { + const qint64 totalTime = mediaObject->totalTime(); + return ((offset * totalTime) / currentVideoSize); } -*/ - void MediaView::findVideoParts() { // parts diff --git a/src/mediaview.h b/src/mediaview.h index 6259e13..2bc3f29 100644 --- a/src/mediaview.h +++ b/src/mediaview.h @@ -72,6 +72,7 @@ public slots: void openWebPage(); void copyWebPage(); void copyVideoLink(); + void openInBrowser(); void shareViaTwitter(); void shareViaFacebook(); void shareViaBuffer(); @@ -114,12 +115,9 @@ private slots: void playbackResume(); void authorPushed(QModelIndex); void searchAgain(); - - /* - void downloadProgress(int percent); void sliderMoved(int value); - void seekTo(int value); - */ + qint64 offsetToTime(qint64 offset); + void startDownloading(); private: MediaView(QWidget *parent = 0); @@ -150,6 +148,8 @@ private: DownloadItem *downloadItem; QList history; QList currentVideoActions; + + qint64 currentVideoSize; }; #endif // __MEDIAVIEW_H__ diff --git a/src/networkaccess.cpp b/src/networkaccess.cpp index 96132c9..ea8b557 100644 --- a/src/networkaccess.cpp +++ b/src/networkaccess.cpp @@ -32,7 +32,7 @@ const QString USER_AGENT = QString(Constants::NAME) + " (" + Constants::WEBSITE + ")"; */ -const QString USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36"; +const QString USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.65 Safari/537.36"; NetworkReply::NetworkReply(QNetworkReply *networkReply) : QObject(networkReply), @@ -141,11 +141,14 @@ QNetworkRequest NetworkAccess::buildRequest(QUrl url) { return request; } -QNetworkReply* NetworkAccess::request(QUrl url, int operation, const QByteArray& body) { +QNetworkReply* NetworkAccess::request(QUrl url, int operation, const QByteArray& body, uint offset) { QNetworkAccessManager *manager = The::networkAccessManager(); QNetworkRequest request = buildRequest(url); + if (offset > 0) + request.setRawHeader("Range", QString("bytes=%1-").arg(offset).toUtf8()); + QNetworkReply *networkReply; switch (operation) { diff --git a/src/networkaccess.h b/src/networkaccess.h index d01f491..7708186 100644 --- a/src/networkaccess.h +++ b/src/networkaccess.h @@ -63,7 +63,7 @@ public: NetworkAccess(QObject* parent = 0); QNetworkReply* request(QUrl url, int operation = QNetworkAccessManager::GetOperation, - const QByteArray &body = QByteArray()); + const QByteArray &body = QByteArray(), uint offset = 0); NetworkReply* get(QUrl url); NetworkReply* head(QUrl url); NetworkReply* post(QUrl url, const QMap& params); diff --git a/src/seekslider.cpp b/src/seekslider.cpp new file mode 100644 index 0000000..281673f --- /dev/null +++ b/src/seekslider.cpp @@ -0,0 +1,15 @@ +#include "seekslider.h" + +class MyProxyStyle : public QProxyStyle { +public: + int styleHint(StyleHint hint, const QStyleOption *option = 0, + const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const { + if (hint == SH_Slider_AbsoluteSetButtons) + return Qt::LeftButton; + return QProxyStyle::styleHint(hint, option, widget, returnData); + } +}; + +SeekSlider::SeekSlider(QWidget *parent) : QSlider(parent) { + setStyle(new MyProxyStyle()); +} diff --git a/src/seekslider.h b/src/seekslider.h new file mode 100644 index 0000000..87e6cb9 --- /dev/null +++ b/src/seekslider.h @@ -0,0 +1,15 @@ +#ifndef SEEKSLIDER_H +#define SEEKSLIDER_H + +#include + +class SeekSlider : public QSlider { + + Q_OBJECT + +public: + SeekSlider(QWidget *parent = 0); + +}; + +#endif // SEEKSLIDER_H -- 2.39.5