File indexing completed on 2024-05-12 16:27:16

0001 /*
0002    SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "showvideowidget.h"
0008 #include "misc/messageattachmentdownloadandsavejob.h"
0009 #include "rocketchataccount.h"
0010 #include "ruqolaglobalconfig.h"
0011 
0012 #include <KLocalizedString>
0013 
0014 #include <KMessageWidget>
0015 #include <QLabel>
0016 #include <QPushButton>
0017 #include <QSlider>
0018 #include <QStyle>
0019 #include <QTime>
0020 #include <QToolButton>
0021 #include <QVBoxLayout>
0022 #include <QVideoWidget>
0023 
0024 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0025 #include <QAudioDevice>
0026 #include <QAudioOutput>
0027 #include <QComboBox>
0028 #include <QMediaDevices>
0029 #endif
0030 
0031 ShowVideoWidget::ShowVideoWidget(RocketChatAccount *account, QWidget *parent)
0032     : QWidget(parent)
0033     , mMediaPlayer(new QMediaPlayer(this
0034 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0035                                     ,
0036                                     QMediaPlayer::VideoSurface
0037 #endif
0038                                     ))
0039     , mPlayButton(new QPushButton(this))
0040     , mPositionSlider(new QSlider(Qt::Horizontal, this))
0041     , mMessageWidget(new KMessageWidget(this))
0042     , mSoundButton(new QToolButton(this))
0043     , mSoundSlider(new QSlider(Qt::Horizontal, this))
0044     , mLabelDuration(new QLabel(this))
0045     , mLabelPercentSound(new QLabel(this))
0046 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0047     , mAudioOutput(new QAudioOutput(this))
0048     , mSoundDeviceComboBox(new QComboBox(this))
0049 #endif
0050     , mRocketChatAccount(account)
0051 {
0052 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0053     mMediaPlayer->setAudioOutput(mAudioOutput);
0054 #endif
0055 
0056     auto mainLayout = new QVBoxLayout(this);
0057     mainLayout->setObjectName(QStringLiteral("mainLayout"));
0058     mainLayout->setContentsMargins({});
0059 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0060     mSoundDeviceComboBox->setObjectName(QStringLiteral("mSoundDeviceComboBox"));
0061     mainLayout->addWidget(mSoundDeviceComboBox);
0062     // TODO initialize it.
0063 #endif
0064 
0065     mLabelDuration->setObjectName(QStringLiteral("mLabelDuration"));
0066 
0067     mMediaPlayer->setObjectName(QStringLiteral("mMediaPlayer"));
0068     auto videoWidget = new QVideoWidget(this);
0069     videoWidget->setObjectName(QStringLiteral("videoWidget"));
0070 
0071     mainLayout->addWidget(videoWidget, 1);
0072 
0073     QBoxLayout *controlLayout = new QHBoxLayout;
0074     controlLayout->setObjectName(QStringLiteral("controlLayout"));
0075     controlLayout->setContentsMargins({});
0076     mainLayout->addLayout(controlLayout);
0077 
0078     mPlayButton->setObjectName(QStringLiteral("mPlayButton"));
0079     mPlayButton->setEnabled(false);
0080     mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
0081 
0082     mLabelDuration->setTextFormat(Qt::PlainText);
0083     mLabelPercentSound->setTextFormat(Qt::PlainText);
0084 
0085     controlLayout->addWidget(mPlayButton);
0086     connect(mPlayButton, &QAbstractButton::clicked, this, &ShowVideoWidget::play);
0087 
0088     mPositionSlider->setObjectName(QStringLiteral("mPositionSlider"));
0089     mPositionSlider->setRange(0, 0);
0090     controlLayout->addWidget(mPositionSlider);
0091     controlLayout->addWidget(mLabelDuration);
0092 
0093     connect(mPositionSlider, &QAbstractSlider::sliderMoved, this, &ShowVideoWidget::setPosition);
0094     connect(mPositionSlider, &QAbstractSlider::valueChanged, this, &ShowVideoWidget::setPosition);
0095 
0096     mMessageWidget->setObjectName(QStringLiteral("mMessageWidget"));
0097     mainLayout->addWidget(mMessageWidget);
0098     mMessageWidget->setVisible(false);
0099     mMessageWidget->setCloseButtonVisible(false);
0100     mMessageWidget->setMessageType(KMessageWidget::Information);
0101     mMessageWidget->setWordWrap(true);
0102 
0103     mMediaPlayer->setVideoOutput(videoWidget);
0104 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0105     connect(mMediaPlayer, &QMediaPlayer::stateChanged, this, &ShowVideoWidget::mediaStateChanged);
0106 #else
0107     connect(mMediaPlayer, &QMediaPlayer::playbackStateChanged, this, &ShowVideoWidget::mediaStateChanged);
0108 #endif
0109     connect(mMediaPlayer, &QMediaPlayer::positionChanged, this, &ShowVideoWidget::slotPositionChanged);
0110     connect(mMediaPlayer, &QMediaPlayer::durationChanged, this, &ShowVideoWidget::slotDurationChanged);
0111 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0112     connect(mMediaPlayer, qOverload<QMediaPlayer::Error>(&QMediaPlayer::error), this, &ShowVideoWidget::handleError);
0113 #else
0114     connect(mMediaPlayer, &QMediaPlayer::errorChanged, this, &ShowVideoWidget::handleError);
0115 #endif
0116     mSoundButton->setCheckable(true);
0117     mSoundButton->setObjectName(QStringLiteral("mSoundButton"));
0118     mSoundButton->setIcon(QIcon::fromTheme(QStringLiteral("player-volume")));
0119 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0120     connect(mSoundButton, &QToolButton::clicked, mMediaPlayer, &QMediaPlayer::setMuted);
0121     connect(mMediaPlayer, &QMediaPlayer::mutedChanged, this, &ShowVideoWidget::slotMuteChanged);
0122 #else
0123     connect(mSoundButton, &QToolButton::clicked, mAudioOutput, &QAudioOutput::setMuted);
0124     connect(mAudioOutput, &QAudioOutput::mutedChanged, this, &ShowVideoWidget::slotMuteChanged);
0125 #endif
0126     controlLayout->addWidget(mSoundButton);
0127     mSoundSlider->setObjectName(QStringLiteral("mSoundSlider"));
0128     mSoundSlider->setRange(0, 100);
0129     mSoundSlider->setValue(RuqolaGlobalConfig::self()->soundVolume());
0130     mSoundSlider->setTickPosition(QSlider::TicksAbove);
0131     connect(mSoundSlider, &QAbstractSlider::sliderMoved, this, &ShowVideoWidget::slotVolumeChanged);
0132     controlLayout->addWidget(mSoundSlider);
0133     controlLayout->addWidget(mLabelPercentSound);
0134     QFontMetrics f(font());
0135     mLabelPercentSound->setFixedWidth(f.horizontalAdvance(QStringLiteral("MMM")));
0136     slotVolumeChanged(mSoundSlider->value());
0137 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0138     initializeAudioOutput();
0139 #endif
0140 }
0141 
0142 ShowVideoWidget::~ShowVideoWidget()
0143 {
0144     RuqolaGlobalConfig::self()->setSoundVolume(mSoundSlider->value());
0145     RuqolaGlobalConfig::self()->save();
0146 }
0147 
0148 void ShowVideoWidget::initializeAudioOutput()
0149 {
0150 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0151     mSoundDeviceComboBox->addItem(i18n("Default"), QVariant::fromValue(QAudioDevice()));
0152     for (const auto &deviceInfo : QMediaDevices::audioOutputs()) {
0153         mSoundDeviceComboBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));
0154     }
0155     connect(mSoundDeviceComboBox, &QComboBox::activated, this, &ShowVideoWidget::audioOutputChanged);
0156 #endif
0157 }
0158 
0159 void ShowVideoWidget::audioOutputChanged(int index)
0160 {
0161 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0162     const auto device = mSoundDeviceComboBox->itemData(index).value<QAudioDevice>();
0163     mMediaPlayer->audioOutput()->setDevice(device);
0164 #endif
0165 }
0166 
0167 void ShowVideoWidget::slotPositionChanged(qint64 progress)
0168 {
0169     const qint64 newValue = progress / 1000;
0170     if (!mPositionSlider->isSliderDown()) {
0171         mPositionSlider->setValue(newValue);
0172     }
0173 
0174     updateDurationInfo(newValue);
0175 }
0176 
0177 void ShowVideoWidget::slotVolumeChanged(int position)
0178 {
0179 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0180     mMediaPlayer->setVolume(position);
0181 #else
0182     mAudioOutput->setVolume(position / 100.0);
0183 #endif
0184     mLabelPercentSound->setText(QStringLiteral("%1%").arg(position));
0185 }
0186 
0187 void ShowVideoWidget::updateDurationInfo(qint64 currentInfo)
0188 {
0189     QString tStr;
0190     if (currentInfo || mDuration) {
0191         const QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60, (currentInfo * 1000) % 1000);
0192         const QTime totalTime((mDuration / 3600) % 60, (mDuration / 60) % 60, mDuration % 60, (mDuration * 1000) % 1000);
0193         QString format = QStringLiteral("mm:ss");
0194         if (mDuration > 3600)
0195             format = QStringLiteral("hh:mm:ss");
0196         tStr = currentTime.toString(format) + QStringLiteral(" / ") + totalTime.toString(format);
0197     }
0198     mLabelDuration->setText(tStr);
0199 }
0200 
0201 void ShowVideoWidget::slotMuteChanged(bool state)
0202 {
0203     mSoundButton->setIcon(state ? QIcon::fromTheme(QStringLiteral("player-volume-muted")) : QIcon::fromTheme(QStringLiteral("player-volume")));
0204 }
0205 
0206 void ShowVideoWidget::slotAttachmentFileDownloadDone(const QString &url)
0207 {
0208     const QUrl localUrl = QUrl::fromLocalFile(url);
0209     Q_EMIT updateTitle(localUrl);
0210     mMessageWidget->setText(QString());
0211     mMessageWidget->hide();
0212     setWindowFilePath(url);
0213 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0214     mMediaPlayer->setMedia(QUrl::fromLocalFile(url));
0215 #else
0216     mMediaPlayer->setSource(localUrl);
0217 #endif
0218     mPlayButton->setEnabled(true);
0219 }
0220 
0221 void ShowVideoWidget::setVideoPath(const QString &url)
0222 {
0223     if (mRocketChatAccount) {
0224         MessageAttachmentDownloadAndSaveJob::MessageAttachmentDownloadJobInfo info;
0225         info.attachmentType = MessageAttachmentDownloadAndSaveJob::AttachmentType::Video;
0226         info.actionType = MessageAttachmentDownloadAndSaveJob::ActionType::DownloadOnly;
0227         info.needToDownloadAttachment = !mRocketChatAccount->attachmentIsInLocalCache(url);
0228         info.parentWidget = this;
0229         info.attachmentPath = url;
0230         auto job = new MessageAttachmentDownloadAndSaveJob(this);
0231         connect(job, &MessageAttachmentDownloadAndSaveJob::attachmentFileDownloadDone, this, &ShowVideoWidget::slotAttachmentFileDownloadDone);
0232         job->setRocketChatAccount(mRocketChatAccount);
0233         job->setInfo(info);
0234         job->start();
0235     } else {
0236         slotAttachmentFileDownloadDone(url);
0237     }
0238 }
0239 
0240 QUrl ShowVideoWidget::videoUrl() const
0241 {
0242 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0243     return QUrl(); // Unused in qt5
0244 #else
0245     return mMediaPlayer->source();
0246 #endif
0247 }
0248 
0249 void ShowVideoWidget::play()
0250 {
0251 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0252     switch (mMediaPlayer->state()) {
0253     case QMediaPlayer::PlayingState:
0254         mMediaPlayer->pause();
0255         break;
0256     default:
0257         mMediaPlayer->play();
0258         break;
0259     }
0260 #else
0261     switch (mMediaPlayer->playbackState()) {
0262     case QMediaPlayer::PlayingState:
0263         mMediaPlayer->pause();
0264         break;
0265     default:
0266         mMediaPlayer->play();
0267         break;
0268     }
0269 #endif
0270 }
0271 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0272 void ShowVideoWidget::mediaStateChanged(QMediaPlayer::State state)
0273 #else
0274 void ShowVideoWidget::mediaStateChanged(QMediaPlayer::PlaybackState state)
0275 #endif
0276 {
0277     switch (state) {
0278     case QMediaPlayer::PlayingState:
0279         mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
0280         break;
0281     default:
0282         mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
0283         break;
0284     }
0285 }
0286 
0287 void ShowVideoWidget::slotDurationChanged(qint64 duration)
0288 {
0289     mDuration = duration / 1000;
0290     mPositionSlider->setMaximum(mDuration);
0291 }
0292 
0293 void ShowVideoWidget::setPosition(int position)
0294 {
0295     mMediaPlayer->setPosition(position * 1000);
0296 }
0297 
0298 void ShowVideoWidget::handleError()
0299 {
0300     mPlayButton->setEnabled(false);
0301     const QString errorString = mMediaPlayer->errorString();
0302     QString message = i18n("Error: ");
0303     if (errorString.isEmpty()) {
0304         message += QStringLiteral(" #") + QString::number(int(mMediaPlayer->error()));
0305     } else {
0306         message += errorString;
0307     }
0308     mMessageWidget->setText(message);
0309     mMessageWidget->animatedShow();
0310 }
0311 
0312 #include "moc_showvideowidget.cpp"