Mostly reimplement Player, begin implementing ViewPlayer UI design

This commit is contained in:
Dark Steveneq
2025-10-17 22:29:12 +02:00
parent c99efcf9e3
commit 63d9c21e98
6 changed files with 158 additions and 86 deletions

1
.gitignore vendored
View File

@@ -85,6 +85,7 @@ CMakeLists.txt.user*
.vs/ .vs/
out/ out/
.cache
.direnv .direnv
dist dist
CMakePresets.json CMakePresets.json

View File

@@ -1,4 +1,5 @@
{ {
"C_Cpp.workspaceParsingPriority": "medium",
"files.associations": { "files.associations": {
"flake.lock": "json", "flake.lock": "json",
"qobject": "cpp", "qobject": "cpp",

View File

@@ -11,6 +11,10 @@ ApplicationWindow {
height: 800 height: 800
title: qsTr("QYouRadio") title: qsTr("QYouRadio")
Component.onCompleted: function() {
console.log(Player.stations)
}
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
@@ -45,21 +49,21 @@ ApplicationWindow {
clip: true clip: true
NumberAnimation { NumberAnimation {
running: Player.currentIndex != null running: Player.playing && tabbar.currentIndex != Player.currentIndex && nowplaying_root.anchors.bottomMargin >= 42
target: nowplaying_root target: nowplaying_root
property: "anchors.bottomMargin" property: "anchors.bottomMargin"
from: 42 from: 42
to: 0 to: 0
duration: 160 duration: 145
} }
NumberAnimation { NumberAnimation {
running: Player.currentIndex == null running: !Player.playing || (tabbar.currentIndex == Player.currentIndex && nowplaying_root.anchors.bottomMargin < 42)
target: nowplaying_root target: nowplaying_root
property: "anchors.bottomMargin" property: "anchors.bottomMargin"
from: 0 from: 0
to: 42 to: 42
duration: 160 duration: 145
} }
Label { Label {
@@ -69,7 +73,7 @@ ApplicationWindow {
anchors.topMargin: 1 anchors.topMargin: 1
anchors.leftMargin: 4 anchors.leftMargin: 4
anchors.rightMargin: 4 anchors.rightMargin: 4
text: (Player.loading ? "Loading " : "Playing ") + (Player.currentIndex != null ? qsTr(Player.currentStream.name) : "") text: (Player.loading ? "Loading " : "Playing ") + qsTr(Player.currentStation.name)
heading: "h5" heading: "h5"
} }
@@ -81,7 +85,7 @@ ApplicationWindow {
anchors.bottomMargin: 1 anchors.bottomMargin: 1
anchors.leftMargin: 4 anchors.leftMargin: 4
// anchors.rightMargin: 4 // anchors.rightMargin: 4
text: (Player.currentIndex != null ? qsTr(Player.currentStream.title) : "") text: qsTr(Player.currentStation.songTitle)
heading: "h3" heading: "h3"
@@ -124,7 +128,6 @@ ApplicationWindow {
} }
} }
SwipeView { SwipeView {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
@@ -134,7 +137,8 @@ ApplicationWindow {
currentIndex: tabbar.currentIndex currentIndex: tabbar.currentIndex
Loader { Loader {
active: tabbar.currentIndex == 0 // active: tabbar.currentIndex == 0
active: true
asynchronous: true asynchronous: true
visible: status == Loader.Ready visible: status == Loader.Ready
sourceComponent: ViewPlayer { sourceComponent: ViewPlayer {
@@ -142,7 +146,8 @@ ApplicationWindow {
} }
} }
Loader { Loader {
active: tabbar.currentIndex == 1 // active: tabbar.currentIndex == 1
active: true
asynchronous: true asynchronous: true
visible: status == Loader.Ready visible: status == Loader.Ready
sourceComponent: ViewPlayer { sourceComponent: ViewPlayer {
@@ -150,18 +155,47 @@ ApplicationWindow {
} }
} }
Loader { Loader {
active: tabbar.currentIndex == 2 // active: tabbar.currentIndex == 2
active: true
asynchronous: true asynchronous: true
visible: status == Loader.Ready visible: status == Loader.Ready
sourceComponent: ViewPlayer { sourceComponent: ViewPlayer {
index: 2 index: 2
} }
} }
Component.onCompleted: contentItem.highlightMoveDuration = 160;
} }
YouAds { RowLayout {
Layout.fillWidth: false Layout.fillWidth: false
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 30
Layout.bottomMargin: 30
width: 220
Button {
Layout.rightMargin: 5
text: Player.loading ? "Loading" : (Player.playing ? "Pause" : "Play")
onClicked: function() {
if (Player.playing) {
Player.stopPlaying();
} else {
Player.startPlaying(tabbar.currentIndex);
}
}
}
Slider {
Layout.fillWidth: true
Layout.leftMargin: 5
from: 0.1
to: 1.1
stepSize: 0.05
value: Player.volume + 0.1
onMoved: Player.volume = value - 0.1
}
} }
} }
} }

View File

@@ -5,7 +5,6 @@ import QtQuick.Layouts 6.8
import QYRComponents 1.0 import QYRComponents 1.0
ColumnLayout { ColumnLayout {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true Layout.fillWidth: true
property var index: null property var index: null
@@ -13,62 +12,40 @@ ColumnLayout {
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
Layout.bottomMargin: 20 Layout.bottomMargin: 20
text: index != null ? Player.streams[index].name : "Invalid station" text: qsTr(Player.stations[index].name)
font.bold: true
heading: "h2" heading: "h2"
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
} }
Label {
Layout.fillWidth: true
text: "Title: " + (index != null ? Player.streams[index].title : "")
heading: "h3"
horizontalAlignment: Text.AlignHCenter
}
Label {
Layout.fillWidth: true
text: "Listeners: " + (index != null ? Player.streams[index].listeners : 0)
heading: "h3"
horizontalAlignment: Text.AlignHCenter
}
Label {
Layout.topMargin: 20
text: (Player.currentIndex != null && Player.currentIndex != parent.index) ? qsTr("Another station is currently playing") : ""
heading: "h2"
horizontalAlignment: Text.AlignHCenter
}
RowLayout {
Layout.fillWidth: false
Layout.alignment: Qt.AlignHCenter
width: 220
Button {
Layout.rightMargin: 5
text: Player.loading ? "Loading" : (Player.playing ? "Pause" : "Play")
onClicked: function() {
if (Player.playing) {
Player.stopPlaying();
} else {
Player.startPlaying(parent.parent.index);
}
}
}
Slider {
Layout.fillWidth: true
Layout.leftMargin: 5
from: 0.1
to: 1.1
stepSize: 0.05
value: Player.volume + 0.1
onMoved: Player.volume = value - 0.1
}
}
Item { Item {
Layout.fillHeight: true Layout.fillHeight: true
} }
}
Label {
Layout.fillWidth: true
text: Player.stations[index].songTitle
font.pixelSize: 72
horizontalAlignment: Text.AlignHCenter
}
Label {
Layout.fillWidth: true
text: "Listening: " + Player.stations[index].listeners
heading: "h2"
horizontalAlignment: Text.AlignHCenter
}
Item {
Layout.fillHeight: true
}
Label {
Layout.fillWidth: true
Layout.topMargin: 20
visible: (Player.currentIndex > -1 && Player.currentIndex != parent.index)
text: qsTr("Another station is currently playing")
heading: "h2"
horizontalAlignment: Text.AlignHCenter
}
}

View File

@@ -2,8 +2,11 @@
// Public // Public
Player::Player(QObject *parent) : QObject(parent), Player::Player(QObject *parent) : QObject(parent),
player(this), player(this), output(this), timer(this), m_currentIndex(0), m_stations({
output(this) Station{"Autoradio", "autoradio", "", 0},
Station{"Live Mix", "live", "", 0},
Station{"Bass Boosted", "bassboosted", "", 0}
})
{ {
QObject::connect(&this->player, &QMediaPlayer::playbackStateChanged, this, [this] () { QObject::connect(&this->player, &QMediaPlayer::playbackStateChanged, this, [this] () {
qDebug("Player::Player()->Lambda: Playing got changed!"); qDebug("Player::Player()->Lambda: Playing got changed!");
@@ -15,9 +18,19 @@ Player::Player(QObject *parent) : QObject(parent),
emit this->volumeChanged(); emit this->volumeChanged();
}); });
this->output.setVolume(0.3); this->timer.start(metadataFetchTimeout);
QObject::connect(&this->timer, &QTimer::timeout, this, [this] () {
qDebug("Player::Player()->Lambda: Timer triggered!");
if (this->m_currentIndex > -1)
{
emit this->currentStationChanged();
}
emit this->stationsChanged();
});
this->output.setVolume(0.2);
this->player.setAudioOutput(&this->output); this->player.setAudioOutput(&this->output);
qDebug("Player::Player(): Constructed"); qDebug("Player::Player(): Constructed");
} }
@@ -42,11 +55,30 @@ void Player::setVolume(float newVolume)
this->output.setVolume(newVolume); this->output.setVolume(newVolume);
} }
const QList<Station> Player::stations() const
{
return this->m_stations;
}
const int Player::currentIndex() const
{
return this->m_currentIndex;
}
const Station* Player::currentStation() const
{
if (this->m_currentIndex < 0 || this->m_currentIndex >= this->m_stations.length())
{
qWarning("Player::currentStation(): Tried accessing out of bounds.");
return nullptr;
}
return &this->m_stations[this->m_currentIndex];
}
// Public slots // Public slots
void Player::startPlaying(u_int8_t index) void Player::startPlaying(u_int8_t index)
{ {
if (index < 0 || index >= maxStations) if (index < 0 || index >= this->m_stations.length())
{ {
qWarning("Player::startPlaying(): Tried accessing out of bounds."); qWarning("Player::startPlaying(): Tried accessing out of bounds.");
return; return;
@@ -54,19 +86,28 @@ void Player::startPlaying(u_int8_t index)
if (this->playing()) { if (this->playing()) {
this->stopPlaying(); this->stopPlaying();
} }
this->player.setSource(QUrl(streamPrefix + stations[index].slug)); this->m_currentIndex = index;
emit this->currentIndexChanged();
emit this->currentStationChanged();
this->player.setSource(QUrl("https://youradio.nonamesoft.xyz/youradio/api/" + this->m_stations[index].m_slug));
this->player.play(); this->player.play();
qDebug("Player::startPlaying(): Starting playing"); qDebug("Player::startPlaying(): Starting playing");
} }
void Player::stopPlaying() void Player::stopPlaying()
{ {
if (this->playing()) if (!this->playing())
{ {
qWarning("Player::stopPlaying(): Prevented redundant stopPlaying() call."); qWarning("Player::stopPlaying(): Prevented redundant stopPlaying() call.");
return; return;
} }
this->player.stop(); this->player.stop();
this->player.setSource(QUrl("")); this->player.setSource(QUrl(""));
this->m_currentIndex = -1;
emit this->currentIndexChanged();
emit this->currentStationChanged();
qDebug("Player::stopPlaying(): Stopping playback..."); qDebug("Player::stopPlaying(): Stopping playback...");
} }

View File

@@ -3,25 +3,27 @@
#include <QtMultimedia> #include <QtMultimedia>
#include <QDebug> #include <QDebug>
#include <QObject> #include <QObject>
#include <QList>
#include <QTimer>
#include <QtQmlIntegration/qqmlintegration.h> #include <QtQmlIntegration/qqmlintegration.h>
constexpr int metadataFetchTimeout = 10000;
constexpr std::string_view metadataEndpoint = "https://youradio.nonamesoft.xyz/youradio/api/status-json.xsl"; constexpr std::string_view metadataEndpoint = "https://youradio.nonamesoft.xyz/youradio/api/status-json.xsl";
constexpr std::string_view streamPrefix = "https://youradio.nonamesoft.xyz/youradio/api/";
struct Station struct Station
{ {
const char name[16]; Q_GADGET
const char slug[16];
QString* songTitle;
u_int8_t listeners;
};
constexpr u_int8_t maxStations = 3; Q_PROPERTY(QString name MEMBER m_name);
Q_PROPERTY(QString slug MEMBER m_slug);
Q_PROPERTY(QString songTitle MEMBER m_songTitle);
Q_PROPERTY(int listeners MEMBER m_listeners);
const Station stations[maxStations] = { public:
{ "Autoradio", "autoradio" , nullptr, 0 }, QString m_name;
{ "Live Mix", "livemix" , nullptr, 0 }, QString m_slug;
{ "Deep Bass", "bassboosted", nullptr, 0 } QString m_songTitle;
int m_listeners;
}; };
class Player : public QObject class Player : public QObject
@@ -30,17 +32,26 @@ class Player : public QObject
QML_SINGLETON QML_SINGLETON
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(bool playing READ playing NOTIFY playingChanged) Q_PROPERTY(bool playing READ playing NOTIFY playingChanged FINAL)
Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged FINAL)
Q_PROPERTY(const QList<Station> stations READ stations NOTIFY stationsChanged FINAL)
Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentIndexChanged FINAL)
Q_PROPERTY(const Station* currentStation READ currentStation NOTIFY currentStationChanged FINAL)
public: public:
Player(QObject *parent = nullptr); Player(QObject *parent = nullptr);
~Player(); ~Player();
bool playing() const; bool playing() const;
float volume() const; float volume() const;
void setVolume(float newVolume); void setVolume(float newVolume);
const QList<Station> stations() const;
const int currentIndex() const;
const Station* currentStation() const;
public slots: public slots:
void startPlaying(u_int8_t index); void startPlaying(u_int8_t index);
void stopPlaying(); void stopPlaying();
@@ -48,8 +59,15 @@ public slots:
signals: signals:
void playingChanged(); void playingChanged();
void volumeChanged(); void volumeChanged();
void stationsChanged();
void currentIndexChanged();
void currentStationChanged();
private: private:
QMediaPlayer player; QMediaPlayer player;
QAudioOutput output; QAudioOutput output;
QTimer timer;
int m_currentIndex;
QList<Station> m_stations;
}; };