File indexing completed on 2024-05-19 05:04:07

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 #include <QAudioDevice>
0025 #include <QAudioOutput>
0026 #include <QComboBox>
0027 #include <QMediaDevices>
0028 
0029 ShowVideoWidget::ShowVideoWidget(RocketChatAccount *account, QWidget *parent)
0030     : QWidget(parent)
0031     , mMediaPlayer(new QMediaPlayer(this))
0032     , mPlayButton(new QPushButton(this))
0033     , mPositionSlider(new QSlider(Qt::Horizontal, this))
0034     , mMessageWidget(new KMessageWidget(this))
0035     , mSoundButton(new QToolButton(this))
0036     , mSoundSlider(new QSlider(Qt::Horizontal, this))
0037     , mLabelDuration(new QLabel(this))
0038     , mLabelPercentSound(new QLabel(this))
0039     , mAudioOutput(new QAudioOutput(this))
0040     , mSoundDeviceComboBox(new QComboBox(this))
0041     , mRocketChatAccount(account)
0042 {
0043     mMediaPlayer->setAudioOutput(mAudioOutput);
0044 
0045     auto mainLayout = new QVBoxLayout(this);
0046     mainLayout->setObjectName(QStringLiteral("mainLayout"));
0047     mainLayout->setContentsMargins({});
0048     mSoundDeviceComboBox->setObjectName(QStringLiteral("mSoundDeviceComboBox"));
0049     mainLayout->addWidget(mSoundDeviceComboBox);
0050     // TODO initialize it.
0051 
0052     mLabelDuration->setObjectName(QStringLiteral("mLabelDuration"));
0053 
0054     mMediaPlayer->setObjectName(QStringLiteral("mMediaPlayer"));
0055     auto videoWidget = new QVideoWidget(this);
0056     videoWidget->setObjectName(QStringLiteral("videoWidget"));
0057 
0058     mainLayout->addWidget(videoWidget, 1);
0059 
0060     QBoxLayout *controlLayout = new QHBoxLayout;
0061     controlLayout->setObjectName(QStringLiteral("controlLayout"));
0062     controlLayout->setContentsMargins({});
0063     mainLayout->addLayout(controlLayout);
0064 
0065     mPlayButton->setObjectName(QStringLiteral("mPlayButton"));
0066     mPlayButton->setEnabled(false);
0067     mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
0068 
0069     mLabelDuration->setTextFormat(Qt::PlainText);
0070     mLabelPercentSound->setTextFormat(Qt::PlainText);
0071 
0072     controlLayout->addWidget(mPlayButton);
0073     connect(mPlayButton, &QAbstractButton::clicked, this, &ShowVideoWidget::play);
0074 
0075     mPositionSlider->setObjectName(QStringLiteral("mPositionSlider"));
0076     mPositionSlider->setRange(0, 0);
0077     controlLayout->addWidget(mPositionSlider);
0078     controlLayout->addWidget(mLabelDuration);
0079 
0080     connect(mPositionSlider, &QAbstractSlider::sliderMoved, this, &ShowVideoWidget::setPosition);
0081     connect(mPositionSlider, &QAbstractSlider::valueChanged, this, &ShowVideoWidget::setPosition);
0082 
0083     mMessageWidget->setObjectName(QStringLiteral("mMessageWidget"));
0084     mainLayout->addWidget(mMessageWidget);
0085     mMessageWidget->setVisible(false);
0086     mMessageWidget->setCloseButtonVisible(false);
0087     mMessageWidget->setMessageType(KMessageWidget::Information);
0088     mMessageWidget->setWordWrap(true);
0089 
0090     mMediaPlayer->setVideoOutput(videoWidget);
0091     connect(mMediaPlayer, &QMediaPlayer::playbackStateChanged, this, &ShowVideoWidget::mediaStateChanged);
0092     connect(mMediaPlayer, &QMediaPlayer::positionChanged, this, &ShowVideoWidget::slotPositionChanged);
0093     connect(mMediaPlayer, &QMediaPlayer::durationChanged, this, &ShowVideoWidget::slotDurationChanged);
0094     connect(mMediaPlayer, &QMediaPlayer::errorChanged, this, &ShowVideoWidget::handleError);
0095     mSoundButton->setCheckable(true);
0096     mSoundButton->setObjectName(QStringLiteral("mSoundButton"));
0097     mSoundButton->setIcon(QIcon::fromTheme(QStringLiteral("player-volume")));
0098     connect(mSoundButton, &QToolButton::clicked, mAudioOutput, &QAudioOutput::setMuted);
0099     connect(mAudioOutput, &QAudioOutput::mutedChanged, this, &ShowVideoWidget::slotMuteChanged);
0100     controlLayout->addWidget(mSoundButton);
0101     mSoundSlider->setObjectName(QStringLiteral("mSoundSlider"));
0102     mSoundSlider->setRange(0, 100);
0103     mSoundSlider->setValue(RuqolaGlobalConfig::self()->soundVolume());
0104     mSoundSlider->setTickPosition(QSlider::TicksAbove);
0105     connect(mSoundSlider, &QAbstractSlider::sliderMoved, this, &ShowVideoWidget::slotVolumeChanged);
0106     controlLayout->addWidget(mSoundSlider);
0107     controlLayout->addWidget(mLabelPercentSound);
0108     QFontMetrics f(font());
0109     mLabelPercentSound->setFixedWidth(f.horizontalAdvance(QStringLiteral("MMM")));
0110     slotVolumeChanged(mSoundSlider->value());
0111     initializeAudioOutput();
0112 }
0113 
0114 ShowVideoWidget::~ShowVideoWidget()
0115 {
0116     RuqolaGlobalConfig::self()->setSoundVolume(mSoundSlider->value());
0117     RuqolaGlobalConfig::self()->save();
0118 }
0119 
0120 void ShowVideoWidget::initializeAudioOutput()
0121 {
0122     mSoundDeviceComboBox->addItem(i18n("Default"), QVariant::fromValue(QAudioDevice()));
0123     for (const auto &deviceInfo : QMediaDevices::audioOutputs()) {
0124         mSoundDeviceComboBox->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo));
0125     }
0126     connect(mSoundDeviceComboBox, &QComboBox::activated, this, &ShowVideoWidget::audioOutputChanged);
0127 }
0128 
0129 void ShowVideoWidget::audioOutputChanged(int index)
0130 {
0131     const auto device = mSoundDeviceComboBox->itemData(index).value<QAudioDevice>();
0132     mMediaPlayer->audioOutput()->setDevice(device);
0133 }
0134 
0135 void ShowVideoWidget::slotPositionChanged(qint64 progress)
0136 {
0137     const qint64 newValue = progress / 1000;
0138     if (!mPositionSlider->isSliderDown()) {
0139         mPositionSlider->setValue(newValue);
0140     }
0141 
0142     updateDurationInfo(newValue);
0143 }
0144 
0145 void ShowVideoWidget::slotVolumeChanged(int position)
0146 {
0147     mAudioOutput->setVolume(position / 100.0);
0148     mLabelPercentSound->setText(QStringLiteral("%1%").arg(position));
0149 }
0150 
0151 void ShowVideoWidget::updateDurationInfo(qint64 currentInfo)
0152 {
0153     QString tStr;
0154     if (currentInfo || mDuration) {
0155         const QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60, (currentInfo * 1000) % 1000);
0156         const QTime totalTime((mDuration / 3600) % 60, (mDuration / 60) % 60, mDuration % 60, (mDuration * 1000) % 1000);
0157         QString format = QStringLiteral("mm:ss");
0158         if (mDuration > 3600)
0159             format = QStringLiteral("hh:mm:ss");
0160         tStr = currentTime.toString(format) + QStringLiteral(" / ") + totalTime.toString(format);
0161     }
0162     mLabelDuration->setText(tStr);
0163 }
0164 
0165 void ShowVideoWidget::slotMuteChanged(bool state)
0166 {
0167     mSoundButton->setIcon(state ? QIcon::fromTheme(QStringLiteral("player-volume-muted")) : QIcon::fromTheme(QStringLiteral("player-volume")));
0168 }
0169 
0170 void ShowVideoWidget::slotAttachmentFileDownloadDone(const QString &url)
0171 {
0172     const QUrl localUrl = QUrl::fromLocalFile(url);
0173     Q_EMIT updateTitle(localUrl);
0174     mMessageWidget->setText(QString());
0175     mMessageWidget->hide();
0176     setWindowFilePath(url);
0177     mMediaPlayer->setSource(localUrl);
0178     mPlayButton->setEnabled(true);
0179 }
0180 
0181 void ShowVideoWidget::setVideoPath(const QString &url)
0182 {
0183     if (mRocketChatAccount) {
0184         MessageAttachmentDownloadAndSaveJob::MessageAttachmentDownloadJobInfo info;
0185         info.attachmentType = MessageAttachmentDownloadAndSaveJob::AttachmentType::Video;
0186         info.actionType = MessageAttachmentDownloadAndSaveJob::ActionType::DownloadOnly;
0187         info.needToDownloadAttachment = !mRocketChatAccount->attachmentIsInLocalCache(url);
0188         info.parentWidget = this;
0189         info.attachmentPath = url;
0190         auto job = new MessageAttachmentDownloadAndSaveJob(this);
0191         connect(job, &MessageAttachmentDownloadAndSaveJob::attachmentFileDownloadDone, this, &ShowVideoWidget::slotAttachmentFileDownloadDone);
0192         job->setRocketChatAccount(mRocketChatAccount);
0193         job->setInfo(info);
0194         job->start();
0195     } else {
0196         slotAttachmentFileDownloadDone(url);
0197     }
0198 }
0199 
0200 QUrl ShowVideoWidget::videoUrl() const
0201 {
0202     return mMediaPlayer->source();
0203 }
0204 
0205 void ShowVideoWidget::play()
0206 {
0207     switch (mMediaPlayer->playbackState()) {
0208     case QMediaPlayer::PlayingState:
0209         mMediaPlayer->pause();
0210         break;
0211     default:
0212         mMediaPlayer->play();
0213         break;
0214     }
0215 }
0216 
0217 void ShowVideoWidget::mediaStateChanged(QMediaPlayer::PlaybackState state)
0218 {
0219     switch (state) {
0220     case QMediaPlayer::PlayingState:
0221         mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
0222         break;
0223     default:
0224         mPlayButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
0225         break;
0226     }
0227 }
0228 
0229 void ShowVideoWidget::slotDurationChanged(qint64 duration)
0230 {
0231     mDuration = duration / 1000;
0232     mPositionSlider->setMaximum(mDuration);
0233 }
0234 
0235 void ShowVideoWidget::setPosition(int position)
0236 {
0237     mMediaPlayer->setPosition(position * 1000);
0238 }
0239 
0240 void ShowVideoWidget::handleError()
0241 {
0242     mPlayButton->setEnabled(false);
0243     const QString errorString = mMediaPlayer->errorString();
0244     QString message = i18n("Error: ");
0245     if (errorString.isEmpty()) {
0246         message += QStringLiteral(" #") + QString::number(int(mMediaPlayer->error()));
0247     } else {
0248         message += errorString;
0249     }
0250     mMessageWidget->setText(message);
0251     mMessageWidget->animatedShow();
0252 }
0253 
0254 #include "moc_showvideowidget.cpp"