File indexing completed on 2025-02-23 04:35:15

0001 // SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
0002 // SPDX-License-Identifier: GPL-3.0-or-later
0003 
0004 #include "videocontroller.h"
0005 
0006 #include "config.h"
0007 #include "plasmatube.h"
0008 
0009 #ifdef HAS_DBUS
0010 #include <KLocalizedString>
0011 #include <QDBusConnection>
0012 #include <QDBusMessage>
0013 #include <QDBusReply>
0014 #include <QGuiApplication>
0015 #endif
0016 
0017 VideoController::VideoController(QObject *parent)
0018     : QObject(parent)
0019     , m_videoModel(new VideoModel(this))
0020     , m_videoQueue(new VideoQueue(this))
0021     , m_mpris(new Mpris2(this))
0022 {
0023     connect(m_videoModel, &VideoModel::videoChanged, this, [this] {
0024         Q_EMIT currentVideoChanged();
0025     });
0026     connect(m_videoQueue, &VideoQueue::currentVideoChanged, this, [this] {
0027         // If the user is attempting to "play" the same video, they probably meant to open the player
0028         if (currentVideo() && currentVideo()->videoId() == m_videoQueue->getCurrentVideoId()) {
0029             openPlayer();
0030             return;
0031         }
0032 
0033         m_videoModel->fetch(m_videoQueue->getCurrentVideoId());
0034 
0035         openPlayer();
0036     });
0037     connect(this, &VideoController::playbackStateChanged, this, [this] {
0038         const bool paused = m_currentPlayer->paused();
0039         const bool durationMatchesPosition = abs(m_currentPlayer->duration() - m_currentPlayer->position()) < 0.1;
0040         const bool notAtBeginning = m_currentPlayer->position() > 1;
0041 
0042         if (paused && durationMatchesPosition && notAtBeginning) {
0043             m_videoQueue->next();
0044         }
0045 
0046         const bool shouldInhibit = currentPlayer() && !currentPlayer()->paused();
0047 
0048 #ifdef HAS_DBUS
0049         if (shouldInhibit) {
0050             QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"),
0051                                                                   QStringLiteral("/ScreenSaver"),
0052                                                                   QStringLiteral("org.freedesktop.ScreenSaver"),
0053                                                                   QStringLiteral("Inhibit"));
0054             message << QGuiApplication::desktopFileName();
0055             message << i18n("Playing video");
0056 
0057             QDBusReply<uint> reply = QDBusConnection::sessionBus().call(message);
0058             if (reply.isValid()) {
0059                 screenSaverDbusCookie = reply.value();
0060             }
0061         } else {
0062             if (screenSaverDbusCookie != 0) {
0063                 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.ScreenSaver"),
0064                                                                       QStringLiteral("/ScreenSaver"),
0065                                                                       QStringLiteral("org.freedesktop.ScreenSaver"),
0066                                                                       QStringLiteral("UnInhibit"));
0067                 message << static_cast<uint>(screenSaverDbusCookie);
0068                 screenSaverDbusCookie = 0;
0069                 QDBusConnection::sessionBus().send(message);
0070             }
0071         }
0072 #endif
0073     });
0074 }
0075 
0076 void VideoController::play(const QString &videoId)
0077 {
0078     if (videoId.isEmpty()) {
0079         qWarning() << "Not trying to play an empty video id.";
0080         return;
0081     }
0082 
0083     m_videoQueue->replace({videoId});
0084 }
0085 
0086 void VideoController::queueNext(const QString &videoId)
0087 {
0088     if (videoId.isEmpty()) {
0089         qWarning() << "Not trying to queue an empty video id.";
0090         return;
0091     }
0092 
0093     m_videoQueue->queueNext(videoId);
0094 }
0095 
0096 void VideoController::togglePlaying()
0097 {
0098     if (m_currentPlayer != nullptr) {
0099         const bool paused = m_currentPlayer->paused();
0100         const bool durationMatchesPosition = abs(m_currentPlayer->duration() - m_currentPlayer->position()) < 0.1;
0101         const bool notAtBeginning = m_currentPlayer->position() > 1;
0102 
0103         if (paused && durationMatchesPosition && notAtBeginning) {
0104             m_currentPlayer->setPosition(0);
0105         }
0106 
0107         if (m_currentPlayer->paused()) {
0108             m_currentPlayer->play();
0109         } else {
0110             m_currentPlayer->pause();
0111         }
0112     }
0113 }
0114 
0115 void VideoController::stop()
0116 {
0117     if (m_currentPlayer != nullptr) {
0118         Q_EMIT m_currentPlayer->command(QStringList() << QStringLiteral("stop"));
0119         videoModel()->clearVideo();
0120     }
0121 }
0122 
0123 void VideoController::previous()
0124 {
0125     m_videoQueue->previous();
0126 }
0127 
0128 void VideoController::next()
0129 {
0130     m_videoQueue->next();
0131 }
0132 
0133 VideoController::VideoMode VideoController::videoMode() const
0134 {
0135     return m_videoMode;
0136 }
0137 
0138 void VideoController::setVideoMode(VideoController::VideoMode mode)
0139 {
0140     if (mode != m_videoMode) {
0141         m_videoMode = mode;
0142         Q_EMIT videoModeChanged();
0143 
0144         openPlayer();
0145     }
0146 }
0147 
0148 MpvObject *VideoController::currentPlayer() const
0149 {
0150     return m_currentPlayer;
0151 }
0152 
0153 void VideoController::setCurrentPlayer(MpvObject *mpvObject)
0154 {
0155     std::optional<qreal> oldPosition;
0156     if (mpvObject != m_currentPlayer) {
0157         // stop existing player
0158         if (m_currentPlayer != nullptr) {
0159             Q_EMIT m_currentPlayer->command(QStringList() << QStringLiteral("stop"));
0160             oldPosition = m_currentPlayer->position();
0161         }
0162 
0163         m_currentPlayer->disconnect(this);
0164 
0165         m_currentPlayer = mpvObject;
0166         Q_EMIT currentPlayerChanged();
0167     }
0168 
0169     m_currentPlayer->setProperty(QStringLiteral("pause"), !PlasmaTube::instance().selectedSource()->preferences().autoPlay());
0170 
0171     Q_EMIT m_currentPlayer->command(QStringList() << QStringLiteral("stop"));
0172     // See https://github.com/mpv-player/mpv/issues/10029 why this is needed
0173     if (PlasmaTube::instance().selectedSource()->type() == VideoSource::Type::PeerTube) {
0174         m_currentPlayer->setProperty(QStringLiteral("stream-lavf-o"), QStringLiteral("seekable=0"));
0175     } else {
0176         m_currentPlayer->setProperty(QStringLiteral("stream-lavf-o"), QStringLiteral(""));
0177     }
0178 
0179     PlasmaTubeSettings settings(KSharedConfig::openConfig(QStringLiteral("plasmatuberc"), KConfig::SimpleConfig, QStandardPaths::AppConfigLocation));
0180     if (settings.proxyType() == 1) {
0181         QString proxyString = QStringLiteral("http://%1:%2@%3:%4")
0182                                   .arg(settings.proxyUser(), settings.proxyPassword(), settings.proxyHost(), QString::number(settings.proxyPort()));
0183         m_currentPlayer->setProperty(QStringLiteral("http-proxy"), proxyString);
0184         m_currentPlayer->setProperty(QStringLiteral("ytdl-raw-options"), proxyString);
0185     }
0186 
0187     Q_EMIT m_currentPlayer->command(QStringList() << QStringLiteral("loadfile")
0188                                                   << PlasmaTube::instance().selectedSource()->api()->resolveVideoUrl(m_videoModel->videoId()));
0189     m_currentPlayer->setProperty(QStringLiteral("ytdl-format"), QStringLiteral("best"));
0190 
0191     connect(m_currentPlayer, &MpvObject::positionChanged, this, &VideoController::positionChanged);
0192     connect(m_currentPlayer, &MpvObject::durationChanged, this, &VideoController::durationChanged);
0193     connect(m_currentPlayer, &MpvObject::pausedChanged, this, &VideoController::playbackStateChanged);
0194     connect(m_currentPlayer, &MpvObject::stoppedChanged, this, &VideoController::playbackStateChanged);
0195 
0196     // Restore old position if we had an existing player
0197     if (oldPosition != std::nullopt) {
0198         qDebug() << "Attempting to restore position to" << *oldPosition;
0199         connect(m_currentPlayer, &MpvObject::stoppedChanged, this, [this, oldPosition] {
0200             m_currentPlayer->seek(*oldPosition);
0201             qDebug() << "Set old position...";
0202 
0203             disconnect(m_currentPlayer);
0204         });
0205     }
0206 }
0207 
0208 VideoItem *VideoController::currentVideo() const
0209 {
0210     return m_videoModel->video();
0211 }
0212 
0213 VideoModel *VideoController::videoModel() const
0214 {
0215     return m_videoModel;
0216 }
0217 
0218 VideoQueue *VideoController::videoQueue() const
0219 {
0220     return m_videoQueue;
0221 }
0222 
0223 void VideoController::openPlayer()
0224 {
0225     if (m_videoMode == VideoMode::Normal) {
0226         Q_EMIT openNormalPlayer();
0227     } else {
0228         Q_EMIT openPiPPlayer();
0229     }
0230 }
0231 
0232 qreal VideoController::position() const
0233 {
0234     return m_currentPlayer->position();
0235 }
0236 
0237 void VideoController::setPosition(qreal position)
0238 {
0239     return m_currentPlayer->setPosition(position);
0240 }
0241 
0242 qreal VideoController::duration() const
0243 {
0244     return m_currentPlayer->duration();
0245 }
0246 
0247 bool VideoController::paused() const
0248 {
0249     return m_currentPlayer->paused();
0250 }
0251 
0252 void VideoController::pause()
0253 {
0254     return m_currentPlayer->pause();
0255 }
0256 
0257 void VideoController::play()
0258 {
0259     return m_currentPlayer->play();
0260 }
0261 
0262 bool VideoController::stopped() const
0263 {
0264     return m_currentPlayer->stopped();
0265 }
0266 
0267 bool VideoController::hasVideo() const
0268 {
0269     return m_currentPlayer != nullptr && currentVideo() != nullptr && currentVideo()->isLoaded();
0270 }
0271 
0272 #include "moc_videocontroller.cpp"