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
+
+ LabelWithStatus
+ QWidget
+
+ 1
+
+
+ m_btnPlayPause
+ m_btnStop
+ m_spinSpeed
+ m_slidProgress
+ m_btnVolume
+ m_slidVolume
+ m_btnDownload
+