#include "mediaview.h"
#include "constants.h"
-#include "downloaditem.h"
#include "downloadmanager.h"
#include "http.h"
#include "loadingwidget.h"
#include "sidebarheader.h"
#include "sidebarwidget.h"
#include "temporary.h"
-#include "videoareawidget.h"
+#include "videoarea.h"
#ifdef APP_ACTIVATION
#include "activation.h"
+#include "activationview.h"
#endif
#ifdef APP_EXTRA
#include "extra.h"
#endif
#include "datautils.h"
#include "idle.h"
+#include "videodefinition.h"
+
+#include "ivchannelsource.h"
+#include "ivsearch.h"
+#include "ivsinglevideosource.h"
+#include "videoapi.h"
MediaView *MediaView::instance() {
static MediaView *i = new MediaView();
}
MediaView::MediaView(QWidget *parent)
- : View(parent), stopped(false), downloadItem(0)
+ : View(parent), splitter(nullptr), stopped(false)
#ifdef APP_SNAPSHOT
,
- snapshotSettings(0)
+ snapshotSettings(nullptr)
#endif
,
pauseTime(0) {
playlistView = new PlaylistView();
playlistView->setParent(this);
connect(playlistView, SIGNAL(activated(const QModelIndex &)),
- SLOT(itemActivated(const QModelIndex &)));
+ SLOT(onItemActivated(const QModelIndex &)));
playlistModel = new PlaylistModel();
- connect(playlistModel, SIGNAL(activeRowChanged(int)), SLOT(activeRowChanged(int)));
+ connect(playlistModel, &PlaylistModel::activeVideoChanged, this,
+ &MediaView::activeVideoChanged);
// needed to restore the selection after dragndrop
connect(playlistModel, SIGNAL(needSelectionFor(QVector<Video *>)),
SLOT(selectVideos(QVector<Video *>)));
SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
SLOT(selectionChanged(const QItemSelection &, const QItemSelection &)));
- connect(playlistView, SIGNAL(authorPushed(QModelIndex)), SLOT(authorPushed(QModelIndex)));
+ connect(playlistView, SIGNAL(authorPushed(QModelIndex)), SLOT(onAuthorPushed(QModelIndex)));
sidebar = new SidebarWidget(this);
sidebar->setPlaylist(playlistView);
+ sidebar->setMaximumWidth(playlistView->minimumWidth() * 3);
connect(sidebar->getRefineSearchWidget(), SIGNAL(searchRefined()), SLOT(searchAgain()));
connect(playlistModel, SIGNAL(haveSuggestions(const QStringList &)), sidebar,
SLOT(showSuggestions(const QStringList &)));
connect(sidebar, SIGNAL(suggestionAccepted(QString)), mainWindow, SLOT(search(QString)));
splitter->addWidget(sidebar);
- videoAreaWidget = new VideoAreaWidget(this);
-
-#ifdef APP_PHONON
- videoWidget = new Phonon::VideoWidget(this);
- videoAreaWidget->setVideoWidget(videoWidget);
-#endif
+ videoAreaWidget = new VideoArea(this);
videoAreaWidget->setListModel(playlistModel);
loadingWidget = new LoadingWidget(this);
#ifdef APP_ACTIVATION
demoTimer = new QTimer(this);
demoTimer->setSingleShot(true);
- connect(demoTimer, &QTimer::timeout, mainWindow, &MainWindow::showActivationView,
+ connect(
+ demoTimer, &QTimer::timeout, this,
+ [this] {
+ if (media->state() != Media::PlayingState) return;
+ media->pause();
+ connect(
+ ActivationView::instance(), &ActivationView::done, media,
+ [this] { media->play(); }, Qt::UniqueConnection);
+ MainWindow::instance()->showActivationView();
+ },
Qt::QueuedConnection);
#endif
currentVideoActions.append(mainWindow->getAction(name));
}
-#ifndef APP_PHONON_SEEK
- QSlider *slider = mainWindow->getSlider();
- connect(slider, SIGNAL(valueChanged(int)), SLOT(sliderMoved(int)));
-#endif
+ for (int i = 0; i < 10; ++i) {
+ QAction *action = new QAction(QString());
+ action->setShortcut(Qt::Key_0 + i);
+ action->setAutoRepeat(false);
+ connect(action, &QAction::triggered, this, [this, i] {
+ qint64 duration = media->duration();
+ // dur : pos = 100 : i*10
+ qint64 position = (duration * (i * 10)) / 100;
+ media->seek(position);
+ });
+ addAction(action);
+ playingVideoActions << action;
+ }
+
+ QAction *leftAction = new QAction(tr("Rewind %1 seconds").arg(10));
+ leftAction->setShortcut(Qt::Key_Left);
+ leftAction->setAutoRepeat(false);
+ connect(leftAction, &QAction::triggered, this, [this] {
+ qint64 position = media->position();
+ position -= 10000;
+ if (position < 0) position = 0;
+ media->seek(position);
+ });
+ addAction(leftAction);
+ playingVideoActions << leftAction;
+
+ QAction *rightAction = new QAction(tr("Fast forward %1 seconds").arg(10));
+ rightAction->setShortcut(Qt::Key_Right);
+ rightAction->setAutoRepeat(false);
+ connect(rightAction, &QAction::triggered, this, [this] {
+ qint64 position = media->position();
+ position += 10000;
+ qint64 duration = media->duration();
+ if (position > duration) position = duration;
+ media->seek(position);
+ });
+ addAction(rightAction);
+ playingVideoActions << rightAction;
}
-#ifdef APP_PHONON
-void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) {
- this->mediaObject = mediaObject;
- Phonon::createPath(mediaObject, videoWidget);
- connect(mediaObject, SIGNAL(finished()), SLOT(playbackFinished()));
- connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
- SLOT(stateChanged(Phonon::State, Phonon::State)));
- connect(mediaObject, SIGNAL(aboutToFinish()), SLOT(aboutToFinish()));
- connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int)));
+void MediaView::setMedia(Media *media) {
+ this->media = media;
+
+ videoWidget = media->videoWidget();
+ videoAreaWidget->setVideoWidget(videoWidget);
+
+ connect(media, &Media::finished, this, &MediaView::onPlaybackFinished);
+ connect(media, &Media::stateChanged, this, &MediaView::mediaStateChanged);
+ connect(media, &Media::aboutToFinish, this, &MediaView::onAboutToFinish);
+ connect(media, &Media::bufferStatus, loadingWidget, &LoadingWidget::bufferStatus);
}
-#endif
SearchParams *MediaView::getSearchParams() {
VideoSource *videoSource = playlistModel->getVideoSource();
- if (videoSource && videoSource->metaObject()->className() == QLatin1String("YTSearch")) {
- YTSearch *search = qobject_cast<YTSearch *>(videoSource);
+ if (!videoSource) return nullptr;
+ auto clazz = videoSource->metaObject()->className();
+ if (clazz == QLatin1String("YTSearch")) {
+ auto search = qobject_cast<YTSearch *>(videoSource);
return search->getSearchParams();
}
- return 0;
+ if (clazz == QLatin1String("IVSearch")) {
+ auto search = qobject_cast<IVSearch *>(videoSource);
+ return search->getSearchParams();
+ }
+ if (clazz == QLatin1String("IVChannelSource")) {
+ auto search = qobject_cast<IVChannelSource *>(videoSource);
+ return search->getSearchParams();
+ }
+ return nullptr;
}
void MediaView::search(SearchParams *searchParams) {
searchParams->keywords().startsWith("https://")) {
QString videoId = YTSearch::videoIdFromUrl(searchParams->keywords());
if (!videoId.isEmpty()) {
- YTSingleVideoSource *singleVideoSource = new YTSingleVideoSource(this);
- singleVideoSource->setVideoId(videoId);
+ VideoSource *singleVideoSource = nullptr;
+ if (VideoAPI::impl() == VideoAPI::YT3) {
+ auto source = new YTSingleVideoSource(this);
+ source->setVideoId(videoId);
+ singleVideoSource = source;
+ } else if (VideoAPI::impl() == VideoAPI::IV) {
+ auto source = new IVSingleVideoSource(this);
+ source->setVideoId(videoId);
+ singleVideoSource = source;
+ }
setVideoSource(singleVideoSource);
+
QTime tstamp = YTSearch::videoTimestampFromUrl(searchParams->keywords());
pauseTime = QTime(0, 0).msecsTo(tstamp);
return;
}
}
}
- YTSearch *ytSearch = new YTSearch(searchParams);
- ytSearch->setAsyncDetails(true);
- connect(ytSearch, SIGNAL(gotDetails()), playlistModel, SLOT(emitDataChanged()));
- setVideoSource(ytSearch);
+
+ VideoSource *search = nullptr;
+ if (VideoAPI::impl() == VideoAPI::YT3) {
+ YTSearch *ytSearch = new YTSearch(searchParams);
+ ytSearch->setAsyncDetails(true);
+ connect(ytSearch, SIGNAL(gotDetails()), playlistModel, SLOT(emitDataChanged()));
+ search = ytSearch;
+ } else if (VideoAPI::impl() == VideoAPI::IV) {
+ if (searchParams->channelId().isEmpty()) {
+ search = new IVSearch(searchParams);
+ } else {
+ search = new IVChannelSource(searchParams);
+ }
+ }
+ setVideoSource(search);
}
void MediaView::setVideoSource(VideoSource *videoSource, bool addToHistory, bool back) {
VideoSource *vs = history.takeLast();
if (!vs->parent()) {
qDebug() << "Deleting VideoSource" << vs->getName() << vs;
- delete vs;
+ vs->deleteLater();
}
}
}
playlistModel->setVideoSource(videoSource);
- QSettings settings;
- if (settings.value("manualplay", false).toBool()) {
- videoAreaWidget->showPickMessage();
+ if (media->state() == Media::StoppedState) {
+ QSettings settings;
+ if (settings.value("manualplay", false).toBool()) {
+ videoAreaWidget->showPickMessage();
+ }
}
+ SearchParams *searchParams = getSearchParams();
+
sidebar->showPlaylist();
- sidebar->getRefineSearchWidget()->setSearchParams(getSearchParams());
+ sidebar->getRefineSearchWidget()->setSearchParams(searchParams);
sidebar->hideSuggestions();
sidebar->getHeader()->updateInfo();
- SearchParams *searchParams = getSearchParams();
bool isChannel = searchParams && !searchParams->channelId().isEmpty();
+ if (isChannel) {
+ updateSubscriptionActionForChannel(searchParams->channelId());
+ }
playlistView->setClickableAuthors(!isChannel);
}
void MediaView::handleError(const QString &message) {
qWarning() << __PRETTY_FUNCTION__ << message;
-#ifdef APP_PHONON_SEEK
- mediaObject->play();
-#else
- QTimer::singleShot(500, this, SLOT(startPlaying()));
+#ifndef QT_NO_DEBUG_OUTPUT
+ MainWindow::instance()->showMessage(message);
#endif
}
-#ifdef APP_PHONON
-void MediaView::stateChanged(Phonon::State newState, Phonon::State oldState) {
- if (pauseTime > 0 && (newState == Phonon::PlayingState || newState == Phonon::BufferingState)) {
- mediaObject->seek(pauseTime);
+void MediaView::mediaStateChanged(Media::State state) {
+ if (pauseTime > 0 && (state == Media::PlayingState || state == Media::BufferingState)) {
+ qDebug() << "Seeking to" << pauseTime;
+ media->seek(pauseTime);
pauseTime = 0;
}
- if (newState == Phonon::PlayingState) {
+ if (state == Media::PlayingState) {
videoAreaWidget->showVideo();
- } else if (newState == Phonon::ErrorState) {
- qWarning() << "Phonon error:" << mediaObject->errorString() << mediaObject->errorType();
- if (mediaObject->errorType() == Phonon::FatalError) handleError(mediaObject->errorString());
+ } else if (state == Media::ErrorState) {
+ handleError(media->errorString());
}
- if (newState == Phonon::PlayingState) {
+ bool enablePlayingVideoActions = state == Media::PlayingState || state == Media::PausedState;
+ for (QAction *action : qAsConst(playingVideoActions))
+ action->setEnabled(enablePlayingVideoActions);
+
+ if (state == Media::PlayingState) {
bool res = Idle::preventDisplaySleep(QString("%1 is playing").arg(Constants::NAME));
if (!res) qWarning() << "Error disabling idle display sleep" << Idle::displayErrorMessage();
- } else if (oldState == Phonon::PlayingState) {
+ } else if (state == Media::PausedState || state == Media::StoppedState) {
bool res = Idle::allowDisplaySleep();
if (!res) qWarning() << "Error enabling idle display sleep" << Idle::displayErrorMessage();
}
}
-#endif
void MediaView::pause() {
-#ifdef APP_PHONON
- switch (mediaObject->state()) {
- case Phonon::PlayingState:
- mediaObject->pause();
+ switch (media->state()) {
+ case Media::PlayingState:
+ media->pause();
pauseTimer.start();
break;
default:
if (pauseTimer.hasExpired(60000)) {
pauseTimer.invalidate();
- connect(playlistModel->activeVideo(), SIGNAL(gotStreamUrl(QUrl)),
- SLOT(resumeWithNewStreamUrl(QUrl)));
+ connect(playlistModel->activeVideo(), &Video::gotStreamUrl, this,
+ &MediaView::resumeWithNewStreamUrl);
playlistModel->activeVideo()->loadStreamUrl();
} else
- mediaObject->play();
+ media->play();
break;
}
-#endif
}
QRegExp MediaView::wordRE(const QString &s) {
videoAreaWidget->update();
errorTimer->stop();
playlistView->selectionModel()->clearSelection();
- if (downloadItem) {
- downloadItem->stop();
- delete downloadItem;
- downloadItem = 0;
- currentVideoSize = 0;
- }
+
MainWindow::instance()->getAction("refineSearch")->setChecked(false);
- updateSubscriptionAction(0, false);
+ updateSubscriptionActionForVideo(nullptr, false);
#ifdef APP_ACTIVATION
demoTimer->stop();
#endif
a->setEnabled(false);
a->setVisible(false);
-#ifdef APP_PHONON
- mediaObject->stop();
- mediaObject->clear();
-#endif
+ media->stop();
+ media->clearQueue();
currentVideoId.clear();
-#ifndef APP_PHONON_SEEK
- QSlider *slider = MainWindow::instance()->getSlider();
- slider->setEnabled(false);
- slider->setValue(0);
-#else
-// Phonon::SeekSlider *slider = MainWindow::instance()->getSeekSlider();
-#endif
-
#ifdef APP_SNAPSHOT
if (snapshotSettings) {
delete snapshotSettings;
- snapshotSettings = 0;
+ snapshotSettings = nullptr;
}
#endif
}
return currentVideoId;
}
-void MediaView::activeRowChanged(int row) {
+void MediaView::activeVideoChanged(Video *video, Video *previousVideo) {
if (stopped) return;
+ media->stop();
errorTimer->stop();
-#ifdef APP_PHONON
- mediaObject->stop();
-#endif
- if (downloadItem) {
- downloadItem->stop();
- delete downloadItem;
- downloadItem = 0;
- currentVideoSize = 0;
+ if (previousVideo && previousVideo != video) {
+ if (previousVideo->isLoadingStreamUrl()) previousVideo->abortLoadStreamUrl();
}
- Video *video = playlistModel->videoAt(row);
- if (!video) return;
-
// optimize window for 16:9 video
adjustWindowSize();
videoAreaWidget->showLoading(video);
- connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)), Qt::UniqueConnection);
+ connect(video, &Video::gotStreamUrl, this, &MediaView::gotStreamUrl, Qt::UniqueConnection);
connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(skip()), Qt::UniqueConnection);
video->loadStreamUrl();
QLatin1String(Constants::NAME));
// ensure active item is visible
+ int row = playlistModel->rowForVideo(video);
if (row != -1) {
QModelIndex index = playlistModel->index(row, 0, QModelIndex());
playlistView->scrollTo(index, QAbstractItemView::EnsureVisible);
// enable/disable actions
MainWindow::instance()
->getAction("download")
- ->setEnabled(DownloadManager::instance()->itemForVideo(video) == 0);
+ ->setEnabled(DownloadManager::instance()->itemForVideo(video) == nullptr);
MainWindow::instance()->getAction("previous")->setEnabled(row > 0);
MainWindow::instance()->getAction("stopafterthis")->setEnabled(true);
MainWindow::instance()->getAction("relatedVideos")->setEnabled(true);
a->setEnabled(enableDownload);
a->setVisible(enableDownload);
- updateSubscriptionAction(video, YTChannel::isSubscribed(video->getChannelId()));
+ updateSubscriptionActionForVideo(video, YTChannel::isSubscribed(video->getChannelId()));
for (QAction *action : currentVideoActions)
action->setEnabled(true);
-#ifndef APP_PHONON_SEEK
- QSlider *slider = MainWindow::instance()->getSlider();
- slider->setEnabled(false);
- slider->setValue(0);
-#endif
-
#ifdef APP_SNAPSHOT
if (snapshotSettings) {
delete snapshotSettings;
- snapshotSettings = 0;
+ snapshotSettings = nullptr;
MainWindow::instance()->adjustStatusBarVisibility();
}
#endif
// see you in gotStreamUrl...
}
-void MediaView::gotStreamUrl(QUrl streamUrl) {
+void MediaView::gotStreamUrl(const QString &streamUrl, const QString &audioUrl) {
if (stopped) return;
- if (!streamUrl.isValid()) {
+ if (streamUrl.isEmpty()) {
+ qWarning() << "Empty stream url";
skip();
return;
}
currentVideoId = video->getId();
-#ifdef APP_PHONON_SEEK
- mediaObject->setCurrentSource(streamUrl);
- mediaObject->play();
-#else
- startDownloading();
-#endif
+ if (audioUrl.isEmpty()) {
+ qDebug() << "Playing" << streamUrl;
+ media->play(streamUrl);
+ } else {
+ qDebug() << "Playing" << streamUrl << audioUrl;
+ media->playSeparateAudioAndVideo(streamUrl, audioUrl);
+ }
// ensure we always have videos ahead
playlistModel->searchNeeded();
}
#ifdef APP_ACTIVATION
- if (!Activation::instance().isActivated() && !demoTimer->isActive()) {
- int ms = (60000 * 5) + (qrand() % (60000 * 5));
+ if (!demoTimer->isActive() && !Activation::instance().isActivated()) {
+ int ms = (60000 * 2) + (QRandomGenerator::global()->generate() % (60000 * 2));
demoTimer->start(ms);
}
#endif
ChannelAggregator::instance()->videoWatched(video);
}
-void MediaView::downloadStatusChanged() {
- // qDebug() << __PRETTY_FUNCTION__;
- switch (downloadItem->status()) {
- case Downloading:
- // qDebug() << "Downloading";
- if (downloadItem->offset() == 0)
- startPlaying();
- else {
-#ifdef APP_PHONON
- // qDebug() << "Seeking to" << downloadItem->offset();
- mediaObject->seek(offsetToTime(downloadItem->offset()));
- mediaObject->play();
-#endif
- }
- break;
- case Starting:
- // qDebug() << "Starting";
- break;
- case Finished:
-// qDebug() << "Finished" << mediaObject->state();
-#ifdef APP_PHONON_SEEK
- MainWindow::instance()->getSeekSlider()->setEnabled(mediaObject->isSeekable());
-#endif
- break;
- case Failed:
- // qDebug() << "Failed";
- skip();
- break;
- case Idle:
- // qDebug() << "Idle";
- break;
- }
-}
-
-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 << QFile::exists(source);
-#ifdef APP_PHONON
- mediaObject->setCurrentSource(QUrl::fromLocalFile(source));
- mediaObject->play();
-#endif
-#ifdef APP_PHONON_SEEK
- MainWindow::instance()->getSeekSlider()->setEnabled(false);
-#else
- QSlider *slider = MainWindow::instance()->getSlider();
- slider->setEnabled(true);
-#endif
-}
-
-void MediaView::itemActivated(const QModelIndex &index) {
+void MediaView::onItemActivated(const QModelIndex &index) {
if (playlistModel->rowExists(index.row())) {
// if it's the current video, just rewind and play
Video *activeVideo = playlistModel->activeVideo();
Video *video = playlistModel->videoAt(index.row());
if (activeVideo && video && activeVideo == video) {
- // mediaObject->seek(0);
- sliderMoved(0);
-#ifdef APP_PHONON
- mediaObject->play();
-#endif
+ media->play();
} else
playlistModel->setActiveRow(index.row());
playlistModel->setActiveRow(prevRow);
}
-void MediaView::aboutToFinish() {
-#ifdef APP_PHONON
- qint64 currentTime = mediaObject->currentTime();
- qint64 totalTime = mediaObject->totalTime();
+void MediaView::onAboutToFinish() {
+ qint64 currentTime = media->position();
+ qint64 totalTime = media->duration();
// qDebug() << __PRETTY_FUNCTION__ << currentTime << totalTime;
if (totalTime < 1 || currentTime + 10000 < totalTime) {
// QTimer::singleShot(500, this, SLOT(playbackResume()));
- mediaObject->seek(currentTime);
- mediaObject->play();
+ media->seek(currentTime);
+ media->play();
}
-#endif
}
-void MediaView::playbackFinished() {
+void MediaView::onPlaybackFinished() {
if (stopped) return;
-#ifdef APP_PHONON
- const qint64 totalTime = mediaObject->totalTime();
- const qint64 currentTime = mediaObject->currentTime();
+ const qint64 totalTime = media->duration();
+ const qint64 currentTime = media->position();
// qDebug() << __PRETTY_FUNCTION__ << mediaObject->currentTime() << totalTime;
// add 10 secs for imprecise Phonon backends (VLC, Xine)
if (currentTime > 0 && currentTime + 10000 < totalTime) {
// mediaObject->seek(currentTime);
- QTimer::singleShot(500, this, SLOT(playbackResume()));
+ QTimer::singleShot(500, this, SLOT(resumePlayback()));
} else {
QAction *stopAfterThisAction = MainWindow::instance()->getAction("stopafterthis");
if (stopAfterThisAction->isChecked()) {
} else
skip();
}
-#endif
}
-void MediaView::playbackResume() {
+void MediaView::resumePlayback() {
if (stopped) return;
-#ifdef APP_PHONON
- const qint64 currentTime = mediaObject->currentTime();
+ const qint64 currentTime = media->position();
// qDebug() << __PRETTY_FUNCTION__ << currentTime;
- if (currentTime > 0) mediaObject->seek(currentTime);
- mediaObject->play();
-#endif
+ if (currentTime > 0) media->seek(currentTime);
+ media->play();
}
void MediaView::openWebPage() {
Video *video = playlistModel->activeVideo();
if (!video) return;
-#ifdef APP_PHONON
- mediaObject->pause();
-#endif
- QString url = video->getWebpage() + QLatin1String("&t=") +
- QString::number(mediaObject->currentTime() / 1000);
+ media->pause();
+ QString url =
+ video->getWebpage() + QLatin1String("&t=") + QString::number(media->position() / 1000);
QDesktopServices::openUrl(url);
}
void MediaView::copyVideoLink() {
Video *video = playlistModel->activeVideo();
if (!video) return;
- QApplication::clipboard()->setText(video->getStreamUrl().toEncoded());
+ QApplication::clipboard()->setText(video->getStreamUrl());
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.");
MainWindow::instance()->showMessage(message);
void MediaView::openInBrowser() {
Video *video = playlistModel->activeVideo();
if (!video) return;
-#ifdef APP_PHONON
- mediaObject->pause();
-#endif
+ media->pause();
QDesktopServices::openUrl(video->getStreamUrl());
}
void MediaView::setSidebarVisibility(bool visible) {
if (sidebar->isVisible() == visible) return;
sidebar->setVisible(visible);
- sidebar->raise();
- playlistView->setFocus();
+ if (visible) {
+ sidebar->move(0, 0);
+ sidebar->resize(sidebar->width(), window()->height());
+ sidebar->raise();
+ playlistView->setFocus();
+ }
}
void MediaView::removeSidebar() {
sidebar->hide();
-#ifndef APP_MAC
sidebar->setParent(window());
- sidebar->move(0, 0);
- sidebar->raise();
-#endif
}
void MediaView::restoreSidebar() {
sidebar->show();
-#ifndef APP_MAC
splitter->insertWidget(0, sidebar);
-#endif
}
bool MediaView::isSidebarVisible() {
void MediaView::saveSplitterState() {
QSettings settings;
- settings.setValue("splitter", splitter->saveState());
+ if (splitter) settings.setValue("splitter", splitter->saveState());
}
void MediaView::downloadVideo() {
Video *video = playlistModel->activeVideo();
if (!video) return;
DownloadManager::instance()->addItem(video);
- MainWindow::instance()->showActionInStatusBar(MainWindow::instance()->getAction("downloads"),
- true);
+ MainWindow::instance()->showActionsInStatusBar({MainWindow::instance()->getAction("downloads")},
+ true);
QString message = tr("Downloading %1").arg(video->getTitle());
MainWindow::instance()->showMessage(message);
}
#ifdef APP_SNAPSHOT
void MediaView::snapshot() {
- qint64 currentTime = mediaObject->currentTime() / 1000;
+ qint64 currentTime = media->position() / 1000;
- QImage image = videoWidget->snapshot();
- if (image.isNull()) {
- qWarning() << "Null snapshot";
- return;
- }
-
- // QPixmap pixmap = QPixmap::grabWindow(videoWidget->winId());
- QPixmap pixmap = QPixmap::fromImage(
- image.scaled(videoWidget->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
- videoAreaWidget->showSnapshotPreview(pixmap);
+ QObject *context = new QObject();
+ connect(media, &Media::snapshotReady, context,
+ [this, currentTime, context](const QImage &image) {
+ context->deleteLater();
- Video *video = playlistModel->activeVideo();
- if (!video) return;
+ if (image.isNull()) {
+ qWarning() << "Null snapshot";
+ return;
+ }
- QString location = SnapshotSettings::getCurrentLocation();
- QDir dir(location);
- if (!dir.exists()) dir.mkpath(location);
- QString basename = video->getTitle();
- QString format = video->getDuration() > 3600 ? "h_mm_ss" : "m_ss";
- basename += " (" + QTime(0, 0, 0).addSecs(currentTime).toString(format) + ")";
- basename = DataUtils::stringToFilename(basename);
- QString filename = location + "/" + basename + ".png";
- qDebug() << filename;
- image.save(filename, "PNG");
-
- if (snapshotSettings) delete snapshotSettings;
- snapshotSettings = new SnapshotSettings(videoWidget);
- snapshotSettings->setSnapshot(pixmap, filename);
- QStatusBar *statusBar = MainWindow::instance()->statusBar();
+ QPixmap pixmap = QPixmap::fromImage(image.scaled(
+ videoWidget->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
+ videoAreaWidget->showSnapshotPreview(pixmap);
+
+ Video *video = playlistModel->activeVideo();
+ if (!video) return;
+
+ QString location = SnapshotSettings::getCurrentLocation();
+ QDir dir(location);
+ if (!dir.exists()) dir.mkpath(location);
+ QString basename = video->getTitle();
+ QString format = video->getDuration() > 3600 ? "h_mm_ss" : "m_ss";
+ basename += " (" + QTime(0, 0, 0).addSecs(currentTime).toString(format) + ")";
+ basename = DataUtils::stringToFilename(basename);
+ QString filename = location + "/" + basename + ".png";
+ qDebug() << filename;
+ image.save(filename, "PNG");
+
+ if (snapshotSettings) delete snapshotSettings;
+ snapshotSettings = new SnapshotSettings(videoWidget);
+ snapshotSettings->setSnapshot(pixmap, filename);
+ QStatusBar *statusBar = MainWindow::instance()->statusBar();
#ifdef APP_EXTRA
- Extra::fadeInWidget(statusBar, statusBar);
+ Extra::fadeInWidget(statusBar, statusBar);
#endif
- statusBar->insertPermanentWidget(0, snapshotSettings);
- snapshotSettings->show();
- MainWindow::instance()->setStatusBarVisibility(true);
-}
+ statusBar->insertPermanentWidget(0, snapshotSettings);
+ snapshotSettings->show();
+ MainWindow::instance()->setStatusBarVisibility(true);
+ }
#endif
+ );
+
+ media->snapshot();
+}
void MediaView::fullscreen() {
- videoAreaWidget->setParent(0);
+ videoAreaWidget->setParent(nullptr);
videoAreaWidget->showFullScreen();
}
-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::resumeWithNewStreamUrl(const QString &streamUrl, const QString &audioUrl) {
+ pauseTime = media->position();
-void MediaView::resumeWithNewStreamUrl(const QUrl &streamUrl) {
- pauseTime = mediaObject->currentTime();
- mediaObject->setCurrentSource(streamUrl);
- mediaObject->play();
+ if (audioUrl.isEmpty()) {
+ qDebug() << "Playing" << streamUrl;
+ media->play(streamUrl);
+ } else {
+ qDebug() << "Playing" << streamUrl << audioUrl;
+ media->playSeparateAudioAndVideo(streamUrl, audioUrl);
+ }
Video *video = static_cast<Video *>(sender());
if (!video) {
video->disconnect(this);
}
-void MediaView::sliderMoved(int value) {
- Q_UNUSED(value);
-#ifdef APP_PHONON
-#ifndef APP_PHONON_SEEK
-
- 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 {
- // qDebug() << "simple seek";
- mediaObject->seek(offsetToTime(offset));
- }
-#endif
-#endif
-}
-
-qint64 MediaView::offsetToTime(qint64 offset) {
-#ifdef APP_PHONON
- const qint64 totalTime = mediaObject->totalTime();
- return ((offset * totalTime) / currentVideoSize);
-#endif
-}
-
void MediaView::findVideoParts() {
Video *video = playlistModel->activeVideo();
if (!video) return;
void MediaView::relatedVideos() {
Video *video = playlistModel->activeVideo();
if (!video) return;
- YTSingleVideoSource *singleVideoSource = new YTSingleVideoSource();
- singleVideoSource->setVideo(video->clone());
- singleVideoSource->setAsyncDetails(true);
- setVideoSource(singleVideoSource);
+
+ if (VideoAPI::impl() == VideoAPI::YT3) {
+ YTSingleVideoSource *singleVideoSource = new YTSingleVideoSource();
+ singleVideoSource->setVideo(video->clone());
+ singleVideoSource->setAsyncDetails(true);
+ setVideoSource(singleVideoSource);
+ } else if (VideoAPI::impl() == VideoAPI::IV) {
+ auto source = new IVSingleVideoSource(this);
+ source->setVideo(video->clone());
+ setVideoSource(source);
+ }
+
MainWindow::instance()->getAction("relatedVideos")->setEnabled(false);
}
QDesktopServices::openUrl(url);
}
-void MediaView::authorPushed(QModelIndex index) {
+void MediaView::onAuthorPushed(QModelIndex index) {
Video *video = playlistModel->videoAt(index.row());
if (!video) return;
search(searchParams);
}
-void MediaView::updateSubscriptionAction(Video *video, bool subscribed) {
+
+void MediaView::updateSubscriptionAction(bool subscribed) {
QAction *subscribeAction = MainWindow::instance()->getAction("subscribeChannel");
QString subscribeTip;
QString subscribeText;
- if (!video) {
+
+ if (currentSubscriptionChannelId.isEmpty()) {
subscribeText = subscribeAction->property("originalText").toString();
subscribeAction->setEnabled(false);
} else if (subscribed) {
- subscribeText = tr("Unsubscribe from %1").arg(video->getChannelTitle());
+ subscribeText = tr("Unsubscribe from %1").arg(currentSubscriptionChannelTitle);
subscribeTip = subscribeText;
subscribeAction->setEnabled(true);
} else {
- subscribeText = tr("Subscribe to %1").arg(video->getChannelTitle());
+ subscribeText = tr("Subscribe to %1").arg(currentSubscriptionChannelTitle);
subscribeTip = subscribeText;
subscribeAction->setEnabled(true);
}
subscribeAction->setStatusTip(subscribeTip);
if (subscribed) {
-#ifdef APP_LINUX_NO
- static QIcon tintedIcon;
- if (tintedIcon.isNull()) {
- QVector<QSize> sizes;
- sizes << QSize(16, 16);
- tintedIcon = IconUtils::tintedIcon("bookmark-new", QColor(254, 240, 0), sizes);
- }
- subscribeAction->setIcon(tintedIcon);
-#else
subscribeAction->setIcon(IconUtils::icon("bookmark-remove"));
-#endif
} else {
subscribeAction->setIcon(IconUtils::icon("bookmark-new"));
}
- IconUtils::setupAction(subscribeAction);
+ MainWindow::instance()->setupAction(subscribeAction);
}
-void MediaView::toggleSubscription() {
+void MediaView::updateSubscriptionActionForChannel(const QString & channelId) {
+ QString channelTitle = tr("channel");
+ YTChannel *channel = YTChannel::forId(channelId);
+ if (nullptr != channel && !channel->getDisplayName().isEmpty()) {
+ channelTitle = channel->getDisplayName();
+ }
+
+ bool subscribed = YTChannel::isSubscribed(channelId);
+
+ currentSubscriptionChannelId = channelId;
+ currentSubscriptionChannelTitle = channelTitle;
+ updateSubscriptionAction(subscribed);
+}
+
+void MediaView::updateSubscriptionActionForVideo(Video *video, bool subscribed) {
+ if (!video) {
+ currentSubscriptionChannelId = "";
+ currentSubscriptionChannelTitle = "";
+ updateSubscriptionAction(false);
+ } else {
+ currentSubscriptionChannelId = video->getChannelId();
+ currentSubscriptionChannelTitle = video->getChannelTitle();
+ updateSubscriptionAction(subscribed);
+ }
+}
+
+void MediaView::reloadCurrentVideo() {
Video *video = playlistModel->activeVideo();
if (!video) return;
- QString userId = video->getChannelId();
- if (userId.isEmpty()) return;
- bool subscribed = YTChannel::isSubscribed(userId);
+
+ int oldFormat = video->getDefinitionCode();
+
+ QObject *context = new QObject();
+ connect(video, &Video::gotStreamUrl, context,
+ [this, oldFormat, video, context](const QString &videoUrl, const QString &audioUrl) {
+ context->deleteLater();
+ if (oldFormat == video->getDefinitionCode()) return;
+ QObject *context2 = new QObject();
+ const qint64 position = media->position();
+ connect(media, &Media::stateChanged, context2,
+ [position, this, context2](Media::State state) {
+ if (state == Media::PlayingState) {
+ media->seek(position);
+ context2->deleteLater();
+ Video *video = playlistModel->activeVideo();
+ QString msg = tr("Switched to %1")
+ .arg(VideoDefinition::forCode(
+ video->getDefinitionCode())
+ .getName());
+ MainWindow::instance()->showMessage(msg);
+ }
+ });
+
+ if (audioUrl.isEmpty()) {
+ media->play(videoUrl);
+ } else {
+ media->playSeparateAudioAndVideo(videoUrl, audioUrl);
+ }
+ });
+ video->loadStreamUrl();
+}
+
+void MediaView::toggleSubscription() {
+ //Video *video = playlistModel->activeVideo();
+ if (currentSubscriptionChannelId.isEmpty()) {
+ return;
+ }
+
+ bool subscribed = YTChannel::isSubscribed(currentSubscriptionChannelId);
if (subscribed) {
- YTChannel::unsubscribe(userId);
+ YTChannel::unsubscribe(currentSubscriptionChannelId);
MainWindow::instance()->showMessage(
- tr("Unsubscribed from %1").arg(video->getChannelTitle()));
+ tr("Unsubscribed from %1").arg(currentSubscriptionChannelTitle));
} else {
- YTChannel::subscribe(userId);
- MainWindow::instance()->showMessage(tr("Subscribed to %1").arg(video->getChannelTitle()));
+ YTChannel::subscribe(currentSubscriptionChannelId);
+ MainWindow::instance()->showMessage(tr("Subscribed to %1").arg(currentSubscriptionChannelTitle));
}
- updateSubscriptionAction(video, !subscribed);
+
+ updateSubscriptionAction(!subscribed);
}
void MediaView::adjustWindowSize() {
+ qDebug() << "Adjusting window size";
Video *video = playlistModel->activeVideo();
if (!video) return;
QWidget *window = this->window();
const double h = (double)videoAreaWidget->height();
const double currentVideoRatio = w / h;
if (currentVideoRatio != ratio) {
+ qDebug() << "Adjust size";
int newHeight = std::round((window->height() - h) + (w / ratio));
window->resize(window->width(), newHeight);
}