Files
bearwave/src/bearplayer.cpp
2026-05-27 07:11:33 +02:00

184 lines
4.8 KiB
C++

// Copyright (c) 2026 Sebastian Palencsar
// SPDX-License-Identifier: GPL-3.0-or-later
#include "bearplayer.h"
#include <QDebug>
#include <QMediaMetaData>
#include <QUrl>
BearPlayer::BearPlayer(QObject *parent) : QObject(parent) {
m_audioOutput = new QAudioOutput(this);
m_mediaPlayer = new QMediaPlayer(this);
m_mediaPlayer->setAudioOutput(m_audioOutput);
m_coverArtFetcher = new CoverArtFetcher(this);
connect(m_coverArtFetcher, &CoverArtFetcher::coverUrlReady, this,
&BearPlayer::onCoverUrlReady);
m_icyReader = new IcyReader(this);
connect(m_icyReader, &IcyReader::metaDataReceived, this,
&BearPlayer::onIcyMetaDataReceived);
connect(m_mediaPlayer, &QMediaPlayer::playbackStateChanged, this,
&BearPlayer::onPlaybackStateChanged);
connect(m_mediaPlayer, &QMediaPlayer::mediaStatusChanged, this,
&BearPlayer::onMediaStatusChanged);
connect(m_mediaPlayer, &QMediaPlayer::metaDataChanged, this,
&BearPlayer::onMetaDataChanged);
m_retryTimer.setSingleShot(true);
m_retryTimer.setInterval(1200);
connect(&m_retryTimer, &QTimer::timeout, this, [this]() {
if (m_lastUrl.isEmpty() || m_playing || m_retryAttempts >= 2) {
return;
}
++m_retryAttempts;
m_mediaPlayer->setSource(QUrl(m_lastUrl));
m_mediaPlayer->play();
qDebug() << "Retry stream" << m_retryAttempts << m_lastName;
});
}
BearPlayer::~BearPlayer() { stop(); }
void BearPlayer::playUrl(const QString &url, const QString &name) {
if (url.isEmpty()) {
return;
}
m_currentStationName = name;
m_lastName = name;
m_lastUrl = url;
m_retryAttempts = 0;
emit currentStationChanged(m_currentStationName);
clearTrackInfo();
m_mediaPlayer->setSource(QUrl(url));
m_mediaPlayer->play();
m_icyReader->start(url);
qDebug() << "Playing:" << name << url;
}
void BearPlayer::stop() {
m_mediaPlayer->stop();
m_icyReader->stop();
m_currentStationName.clear();
m_lastName.clear();
m_lastUrl.clear();
m_retryAttempts = 0;
emit currentStationChanged(QString());
clearTrackInfo();
}
void BearPlayer::togglePlayPause() {
if (m_playing) {
m_mediaPlayer->pause();
} else {
m_mediaPlayer->play();
}
}
void BearPlayer::setVolume(qreal vol) {
m_audioOutput->setVolume(qBound(0.0, vol, 1.0));
emit volumeChanged(m_audioOutput->volume());
}
void BearPlayer::onPlaybackStateChanged(QMediaPlayer::PlaybackState state) {
m_playing = (state == QMediaPlayer::PlayingState);
emit playingChanged(m_playing);
if (state == QMediaPlayer::StoppedState) {
scheduleRetry();
}
}
void BearPlayer::onMediaStatusChanged(QMediaPlayer::MediaStatus status) {
if (status == QMediaPlayer::InvalidMedia) {
m_playing = false;
emit playingChanged(m_playing);
scheduleRetry();
}
}
void BearPlayer::scheduleRetry() {
if (m_lastUrl.isEmpty() || m_retryAttempts >= 2 || m_playing) {
return;
}
if (!m_retryTimer.isActive()) {
m_retryTimer.start();
}
}
void BearPlayer::onMetaDataChanged() {
QMediaMetaData meta = m_mediaPlayer->metaData();
QString artist = meta.stringValue(QMediaMetaData::ContributingArtist);
if (artist.isEmpty()) {
artist = meta.stringValue(QMediaMetaData::Author);
}
QString title = meta.stringValue(QMediaMetaData::Title);
// Ignore completely empty metadata from QMediaPlayer so we don't overwrite
// IcyReader
if (artist.isEmpty() && title.isEmpty()) {
return;
}
if (m_currentTrackArtist == artist && m_currentTrackTitle == title) {
return;
}
m_currentTrackArtist = artist;
m_currentTrackTitle = title;
m_currentCoverArtUrl.clear();
m_coverArtFetcher->fetch(artist, title);
emit trackInfoChanged();
}
void BearPlayer::onCoverUrlReady(const QString &url) {
if (m_currentCoverArtUrl != url) {
m_currentCoverArtUrl = url;
emit trackInfoChanged();
}
}
void BearPlayer::onIcyMetaDataReceived(const QString &artist,
const QString &title) {
if (m_currentTrackArtist == artist && m_currentTrackTitle == title) {
return;
}
m_currentTrackArtist = artist;
m_currentTrackTitle = title;
m_currentCoverArtUrl.clear();
m_coverArtFetcher->fetch(artist, title);
emit trackInfoChanged();
}
QString BearPlayer::currentNowPlaying() const {
if (!m_currentTrackArtist.isEmpty() && !m_currentTrackTitle.isEmpty()) {
return m_currentTrackArtist + QStringLiteral(" - ") + m_currentTrackTitle;
}
if (!m_currentTrackTitle.isEmpty()) {
return m_currentTrackTitle;
}
if (!m_currentTrackArtist.isEmpty()) {
return m_currentTrackArtist;
}
return QString();
}
void BearPlayer::clearTrackInfo() {
if (m_currentTrackArtist.isEmpty() && m_currentTrackTitle.isEmpty()) {
return;
}
m_currentTrackArtist.clear();
m_currentTrackTitle.clear();
m_currentCoverArtUrl.clear();
m_coverArtFetcher->fetch(QString(), QString());
emit trackInfoChanged();
}