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

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