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"