diff --git a/src/bearplayer.cpp b/src/bearplayer.cpp index a82d928..5523226 100644 --- a/src/bearplayer.cpp +++ b/src/bearplayer.cpp @@ -1,195 +1,183 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "bearplayer.h" -#include #include #include +#include +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); -BearPlayer::BearPlayer(QObject *parent) - : QObject(parent) -{ - m_audioOutput = new QAudioOutput(this); - m_mediaPlayer = new QMediaPlayer(this); - m_mediaPlayer->setAudioOutput(m_audioOutput); + m_icyReader = new IcyReader(this); + connect(m_icyReader, &IcyReader::metaDataReceived, this, + &BearPlayer::onIcyMetaDataReceived); - m_coverArtFetcher = new CoverArtFetcher(this); - connect(m_coverArtFetcher, &CoverArtFetcher::coverUrlReady, this, &BearPlayer::onCoverUrlReady); + 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_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_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_currentStationName = name; - m_lastName = name; - m_lastUrl = url; - m_retryAttempts = 0; - emit currentStationChanged(m_currentStationName); - clearTrackInfo(); - - m_mediaPlayer->setSource(QUrl(url)); + ++m_retryAttempts; + m_mediaPlayer->setSource(QUrl(m_lastUrl)); m_mediaPlayer->play(); - m_icyReader->start(url); - - qDebug() << "Playing:" << name << url; + qDebug() << "Retry stream" << m_retryAttempts << m_lastName; + }); } -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(); +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::togglePlayPause() -{ - if (m_playing) { - m_mediaPlayer->pause(); - } else { - m_mediaPlayer->play(); - } +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::setVolume(qreal vol) -{ - m_audioOutput->setVolume(qBound(0.0, vol, 1.0)); - emit volumeChanged(m_audioOutput->volume()); +void BearPlayer::togglePlayPause() { + if (m_playing) { + m_mediaPlayer->pause(); + } else { + m_mediaPlayer->play(); + } } -void BearPlayer::onPlaybackStateChanged(QMediaPlayer::PlaybackState state) -{ - m_playing = (state == QMediaPlayer::PlayingState); +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); - - if (state == QMediaPlayer::StoppedState) { - scheduleRetry(); - } + 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::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::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); +void BearPlayer::onCoverUrlReady(const QString &url) { + if (m_currentCoverArtUrl != url) { + m_currentCoverArtUrl = url; 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(); } -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(); } -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(); +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(); } diff --git a/src/bearplayer.h b/src/bearplayer.h index 1637b4b..a45f02f 100644 --- a/src/bearplayer.h +++ b/src/bearplayer.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef BEARPLAYER_H #define BEARPLAYER_H diff --git a/src/bearwavecontroladaptor.cpp b/src/bearwavecontroladaptor.cpp index b72ee85..804703c 100644 --- a/src/bearwavecontroladaptor.cpp +++ b/src/bearwavecontroladaptor.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "bearwavecontroladaptor.h" #include "radiobackend.h" diff --git a/src/bearwavecontroladaptor.h b/src/bearwavecontroladaptor.h index 216e4a4..e3493cd 100644 --- a/src/bearwavecontroladaptor.h +++ b/src/bearwavecontroladaptor.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef BEARWAVECONTROLADAPTOR_H #define BEARWAVECONTROLADAPTOR_H diff --git a/src/coverartfetcher.cpp b/src/coverartfetcher.cpp index 3f8e08f..53eb424 100644 --- a/src/coverartfetcher.cpp +++ b/src/coverartfetcher.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "coverartfetcher.h" #include #include diff --git a/src/coverartfetcher.h b/src/coverartfetcher.h index 84c0e5a..8486d59 100644 --- a/src/coverartfetcher.h +++ b/src/coverartfetcher.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef COVERARTFETCHER_H #define COVERARTFETCHER_H diff --git a/src/icyreader.cpp b/src/icyreader.cpp index 2d5f413..8f5860f 100644 --- a/src/icyreader.cpp +++ b/src/icyreader.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "icyreader.h" #include #include diff --git a/src/icyreader.h b/src/icyreader.h index 23a2e3e..04bbad8 100644 --- a/src/icyreader.h +++ b/src/icyreader.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef ICYREADER_H #define ICYREADER_H diff --git a/src/main.cpp b/src/main.cpp index d2d3162..bfeff6d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include #include #include diff --git a/src/mprisadaptor.cpp b/src/mprisadaptor.cpp index 816fc76..8bc7dcf 100644 --- a/src/mprisadaptor.cpp +++ b/src/mprisadaptor.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "mprisadaptor.h" #include "radiobackend.h" diff --git a/src/mprisadaptor.h b/src/mprisadaptor.h index bd3ffb0..48c27e1 100644 --- a/src/mprisadaptor.h +++ b/src/mprisadaptor.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef MPRISADAPTOR_H #define MPRISADAPTOR_H diff --git a/src/notificationmanager.cpp b/src/notificationmanager.cpp index e02142a..4a26cae 100644 --- a/src/notificationmanager.cpp +++ b/src/notificationmanager.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "notificationmanager.h" #include "radiobackend.h" #include "bearplayer.h" diff --git a/src/notificationmanager.h b/src/notificationmanager.h index 4c53dc2..09a0df2 100644 --- a/src/notificationmanager.h +++ b/src/notificationmanager.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef NOTIFICATIONMANAGER_H #define NOTIFICATIONMANAGER_H diff --git a/src/qml/Main.qml b/src/qml/Main.qml index b2a9e69..f76e447 100644 --- a/src/qml/Main.qml +++ b/src/qml/Main.qml @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 diff --git a/src/radiobackend.cpp b/src/radiobackend.cpp index e801878..96f0331 100644 --- a/src/radiobackend.cpp +++ b/src/radiobackend.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "radiobackend.h" #include diff --git a/src/radiobackend.h b/src/radiobackend.h index 5f127a5..d172f5a 100644 --- a/src/radiobackend.h +++ b/src/radiobackend.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef RADIOBACKEND_H #define RADIOBACKEND_H diff --git a/src/radiobrowser.cpp b/src/radiobrowser.cpp index 0e9c5ed..6b6361e 100644 --- a/src/radiobrowser.cpp +++ b/src/radiobrowser.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "radiobrowser.h" #include diff --git a/src/radiobrowser.h b/src/radiobrowser.h index e9db7bc..dfdadfe 100644 --- a/src/radiobrowser.h +++ b/src/radiobrowser.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef RADIOBROWSER_H #define RADIOBROWSER_H diff --git a/src/radiostation.cpp b/src/radiostation.cpp index 6648bbb..933f157 100644 --- a/src/radiostation.cpp +++ b/src/radiostation.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #include "radiostation.h" #include diff --git a/src/radiostation.h b/src/radiostation.h index a227821..872247c 100644 --- a/src/radiostation.h +++ b/src/radiostation.h @@ -1,3 +1,6 @@ +// Copyright (c) 2026 Sebastian Palencsar +// SPDX-License-Identifier: GPL-3.0-or-later + #ifndef RADIOSTATION_H #define RADIOSTATION_H