diff --git a/localization/rssguard_en.ts b/localization/rssguard_en.ts index 2fee15762..efaf0f0c5 100644 --- a/localization/rssguard_en.ts +++ b/localization/rssguard_en.ts @@ -4504,87 +4504,127 @@ Login tokens expiration: %2 - + + Play/pause + + + + + Stop + Stop + + + + Speed + + + + + Progress + + + + + Duration + + + + + Mute/unmute + + + + + Volume + + + + + Download + + + + Cannot load media (missing codecs) - + Unrecognized format - + Network problem - + Access denied - + Service is missing - + This is playlist - + No errors - + Unknown error - + No media - + Loading... - + Media loaded - + Media stalled - + Buffering... - + Loaded - + Ended - + Media is invalid - + Unknown diff --git a/src/librssguard/gui/reusable/mediaplayer.cpp b/src/librssguard/gui/reusable/mediaplayer.cpp index 7de1be1a0..f18e79235 100644 --- a/src/librssguard/gui/reusable/mediaplayer.cpp +++ b/src/librssguard/gui/reusable/mediaplayer.cpp @@ -82,6 +82,10 @@ void MediaPlayer::muteUnmute() { m_muted = !m_muted; } +void MediaPlayer::setSpeed(int speed) { + m_player->setPlaybackRate(convertSpeed(speed)); +} + void MediaPlayer::setVolume(int volume) { #if QT_VERSION_MAJOR == 6 m_player->audioOutput()->setVolume(convertSliderVolume(volume)); @@ -96,19 +100,36 @@ void MediaPlayer::seek(int position) { m_player->setPosition(convertSliderProgress(position)); } +void MediaPlayer::onPlaybackRateChanged(qreal speed) { + m_ui.m_spinSpeed->blockSignals(true); + m_ui.m_spinSpeed->setValue(convertSpinSpeed(speed)); + m_ui.m_spinSpeed->blockSignals(false); +} + void MediaPlayer::onDurationChanged(qint64 duration) { m_ui.m_slidProgress->blockSignals(true); - m_ui.m_slidProgress->setMaximum(duration / 1000); + m_ui.m_slidProgress->setMaximum(convertDuration(duration)); m_ui.m_slidProgress->blockSignals(false); + + updateTimeAndProgress(convertToSliderProgress(m_player->position()), convertDuration(duration)); +} + +void MediaPlayer::onPositionChanged(qint64 position) { + m_ui.m_slidProgress->blockSignals(true); + m_ui.m_slidProgress->setValue(convertToSliderProgress(position)); + m_ui.m_slidProgress->blockSignals(false); + + updateTimeAndProgress(convertToSliderProgress(position), convertDuration(m_player->duration())); +} + +void MediaPlayer::updateTimeAndProgress(int progress, int total) { + m_ui.m_lblTime->setText(QSL("%1/%2").arg(QDateTime::fromSecsSinceEpoch(progress).toUTC().toString("hh:mm:ss"), + QDateTime::fromSecsSinceEpoch(total).toUTC().toString("hh:mm:ss"))); } void MediaPlayer::onErrorOccurred(QMediaPlayer::Error error, const QString& error_string) { - if (error_string.isEmpty()) { - m_ui.m_lblStatus->setText(errorToString(error)); - } - else { - m_ui.m_lblStatus->setText(error_string); - } + QString err = error_string.isEmpty() ? errorToString(error) : error_string; + m_ui.m_lblStatus->setStatus(WidgetWithStatus::StatusType::Error, err, err); } void MediaPlayer::onAudioAvailable(bool available) { @@ -121,7 +142,12 @@ void MediaPlayer::onVideoAvailable(bool available) { } void MediaPlayer::onMediaStatusChanged(QMediaPlayer::MediaStatus status) { - m_ui.m_lblStatus->setText(mediaStatusToString(status)); + QString st = mediaStatusToString(status); + m_ui.m_lblStatus->setStatus(status == QMediaPlayer::MediaStatus::InvalidMedia + ? WidgetWithStatus::StatusType::Error + : WidgetWithStatus::StatusType::Information, + st, + st); } void MediaPlayer::onPlaybackStateChanged(QMediaPlayer::PLAYBACK_STATE state) { @@ -143,16 +169,22 @@ void MediaPlayer::onPlaybackStateChanged(QMediaPlayer::PLAYBACK_STATE state) { } } -void MediaPlayer::onPositionChanged(qint64 position) { - m_ui.m_slidProgress->blockSignals(true); - m_ui.m_slidProgress->setValue(convertToSliderProgress(position)); - m_ui.m_slidProgress->blockSignals(false); -} - int MediaPlayer::convertToSliderProgress(qint64 player_progress) const { return player_progress / 1000; } +int MediaPlayer::convertDuration(qint64 duration) const { + return duration / 1000; +} + +qreal MediaPlayer::convertSpeed(int speed) const { + return speed / 100.0; +} + +int MediaPlayer::convertSpinSpeed(qreal speed) const { + return speed * 100; +} + void MediaPlayer::onSeekableChanged(bool seekable) { m_ui.m_slidProgress->setEnabled(seekable); @@ -264,6 +296,7 @@ void MediaPlayer::createConnections() { connect(m_player, &QMediaPlayer::mediaStatusChanged, this, &MediaPlayer::onMediaStatusChanged); connect(m_player, &QMediaPlayer::positionChanged, this, &MediaPlayer::onPositionChanged); connect(m_player, &QMediaPlayer::seekableChanged, this, &MediaPlayer::onSeekableChanged); + connect(m_player, &QMediaPlayer::playbackRateChanged, this, &MediaPlayer::onPlaybackRateChanged); connect(m_ui.m_btnPlayPause, &PlainToolButton::clicked, this, &MediaPlayer::playPause); connect(m_ui.m_btnStop, &PlainToolButton::clicked, this, &MediaPlayer::stop); @@ -271,4 +304,5 @@ void MediaPlayer::createConnections() { connect(m_ui.m_btnVolume, &PlainToolButton::clicked, this, &MediaPlayer::muteUnmute); connect(m_ui.m_slidVolume, &QSlider::valueChanged, this, &MediaPlayer::setVolume); connect(m_ui.m_slidProgress, &QSlider::valueChanged, this, &MediaPlayer::seek); + connect(m_ui.m_spinSpeed, &QSpinBox::valueChanged, this, &MediaPlayer::setSpeed); } diff --git a/src/librssguard/gui/reusable/mediaplayer.h b/src/librssguard/gui/reusable/mediaplayer.h index a2e61b8cd..88a75268e 100644 --- a/src/librssguard/gui/reusable/mediaplayer.h +++ b/src/librssguard/gui/reusable/mediaplayer.h @@ -36,6 +36,7 @@ class MediaPlayer : public TabContent { void stop(); void download(); void muteUnmute(); + void setSpeed(int speed); // NOTE: Volume is from 0 to 100 taken directly from slider or // elsewhere. @@ -45,14 +46,13 @@ class MediaPlayer : public TabContent { // for "int" data type, therefore we seek by second. void seek(int position); + void onPlaybackRateChanged(qreal speed); void onDurationChanged(qint64 duration); void onErrorOccurred(QMediaPlayer::Error error, const QString& error_string = {}); void onAudioAvailable(bool available); void onVideoAvailable(bool available); void onMediaStatusChanged(QMediaPlayer::MediaStatus status); - void onPlaybackStateChanged(QMediaPlayer::PLAYBACK_STATE state); - void onPositionChanged(qint64 position); void onSeekableChanged(bool seekable); @@ -63,10 +63,14 @@ class MediaPlayer : public TabContent { float convertSliderVolume(int slider_volume) const; qint64 convertSliderProgress(int slider_progress) const; int convertToSliderProgress(qint64 player_progress) const; + int convertDuration(qint64 duration) const; + qreal convertSpeed(int speed) const; + int convertSpinSpeed(qreal speed) const; QString errorToString(QMediaPlayer::Error error) const; QString mediaStatusToString(QMediaPlayer::MediaStatus status) const; + void updateTimeAndProgress(int progress, int total); void setupIcons(); void createConnections(); diff --git a/src/librssguard/gui/reusable/mediaplayer.ui b/src/librssguard/gui/reusable/mediaplayer.ui index 72b2f139d..4f78498d5 100644 --- a/src/librssguard/gui/reusable/mediaplayer.ui +++ b/src/librssguard/gui/reusable/mediaplayer.ui @@ -27,26 +27,73 @@ - + - + + + Play/pause + + - + + + Stop + + + + + + + Speed + + + true + + + QAbstractSpinBox::CorrectToNearestValue + + + % + + + 1 + + + 1000 + + + 10 + + + 100 + + + + Progress + Qt::Horizontal - + + + Duration + + - + + + Mute/unmute + + @@ -56,6 +103,9 @@ 0 + + Volume + 100 @@ -71,7 +121,11 @@ - + + + Download + + @@ -89,7 +143,22 @@ QToolButton
plaintoolbutton.h
+ + LabelWithStatus + QWidget +
labelwithstatus.h
+ 1 +
+ + m_btnPlayPause + m_btnStop + m_spinSpeed + m_slidProgress + m_btnVolume + m_slidVolume + m_btnDownload +