Begin implementing UI design, implement metadata fetching

This commit is contained in:
Dark Steveneq
2025-10-14 21:02:58 +02:00
parent d46d37c465
commit 18ad69ba5d
4 changed files with 165 additions and 92 deletions

136
Main.qml
View File

@@ -13,80 +13,96 @@ ApplicationWindow {
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
RowLayout {
Rectangle {
Layout.fillWidth: true
height: 43
Label {
text: "QYouRadio"
heading: "h3"
font.bold: true
}
color: Colors.surface0
// Item {
// Layout.fillWidth: true
// }
Rectangle {
Layout.leftMargin: 20
visible: Player.currentIndex != null
color: Colors.primary
radius: 5
clip: true
width: 320
height: 36
RowLayout {
anchors.fill: parent
anchors.margins: 3
Label {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 1
anchors.leftMargin: 4
anchors.rightMargin: 4
text: (Player.loading ? "Loading " : "Playing ") + (Player.currentIndex != null ? qsTr(Player.currentStream.name) : "")
heading: "h2"
Layout.leftMargin: 5
text: "QYouRadio"
heading: "h1"
// font.bold: true
}
Label {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottomMargin: 1
anchors.leftMargin: 4
anchors.rightMargin: 4
text: "Title will go here"
heading: "h4"
}
}
Rectangle {
Layout.leftMargin: 5
visible: Player.currentIndex != null
color: Colors.primary
radius: 5
clip: true
width: 320
height: 36
Item {
Layout.fillWidth: true
}
Label {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 1
anchors.leftMargin: 4
anchors.rightMargin: 4
text: (Player.loading ? "Loading " : "Playing ") + (Player.currentIndex != null ? qsTr(Player.currentStream.name) : "")
heading: "h5"
}
TabBar {
id: tabbar
spacing: 10
Label {
id: nowplaying_title
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottomMargin: 1
anchors.leftMargin: 4
anchors.rightMargin: 4
text: (Player.currentIndex != null ? qsTr(Player.currentStream.title) : "")
heading: "h3"
background: Item{}
SequentialAnimation {
running: true
loops: Animation.Infinite
TabButton {
text: qsTr("Autoradio")
NumberAnimation { target: nowplaying_title; property: "anchors.rightMargin"; from: 0; to: 320; duration: 5000 }
NumberAnimation { target: nowplaying_title; property: "anchors.leftMargin"; from: 0; to: 320; duration: 5000 }
}
}
}
TabButton {
text: qsTr("Live Mix")
Item {
Layout.fillWidth: true
}
TabButton {
text: qsTr("Deep Bass")
}
}
Button {
text: "S"
onClicked: function() {
var component = Qt.createComponent("ViewSettings.qml")
var window = component.createObject(root)
window.show()
TabBar {
id: tabbar
spacing: 10
background: Item{}
TabButton {
text: qsTr("Autoradio")
}
TabButton {
text: qsTr("Live Mix")
}
TabButton {
text: qsTr("Deep Bass")
}
TabButton {
text: qsTr("Settings")
}
}
// Button {
// text: "S"
// onClicked: function() {
// var component = Qt.createComponent("ViewSettings.qml")
// var window = component.createObject(root)
// window.show()
// }
// }
}
}
@@ -94,7 +110,7 @@ ApplicationWindow {
SwipeView {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 10
Layout.margins: 20
interactive: false
currentIndex: tabbar.currentIndex

View File

@@ -3,38 +3,39 @@ import QtMultimedia 6.8
pragma Singleton
MediaPlayer {
readonly property var streams: ([
Item {
readonly property string streamURLPrefix: "https://youradio.nonamesoft.xyz/youradio/api/"
readonly property string metadataURL: "https://youradio.nonamesoft.xyz/youradio/api/status-json.xsl"
readonly property var slugLookup: ({
"autoradio": 0,
"live": 1,
"bassboosted": 2
})
property var streams: ([
{
name: "Autoradio",
streamURL: "https://youradio.nonamesoft.xyz/api/autoradio"
slug: "autoradio",
title: "",
listeners: 0
},
{
name: "Live Mix",
streamURL: "https://youradio.nonamesoft.xyz/api/live"
slug: "live",
title: "",
listeners: 0
},
{
name: "Deep Bass",
streamURL: "https://youradio.nonamesoft.xyz/api/bassboosted"
slug: "bassboosted",
title: "",
listeners: 0
}
])
property var failedConnAttempts: 0
id: player
source: ""
audioOutput: AudioOutput {
id: output
volume: 0.4
}
onErrorOccurred: function(error, errorString) {
const index = currentIndex
currentIndex = null
currentIndex = index;
}
property alias playing: player.playing
property alias volume: output.volume
readonly property var loading: mediaStatus == Qt.LoadingMedia
readonly property var loading: player.mediaStatus == Qt.LoadingMedia
property var currentIndex: null
property var currentStream: null
@@ -49,7 +50,7 @@ MediaPlayer {
print("Starting playing stream no. " + index);
currentIndex = index;
currentStream = streams[index];
source = currentStream.streamURL;
player.source = streamURLPrefix + currentStream.slug;
player.play();
}
@@ -60,7 +61,63 @@ MediaPlayer {
print("Stopping playback");
currentIndex = null;
currentStream = null;
source = "";
player.source = "";
player.stop()
}
MediaPlayer {
id: player
source: ""
audioOutput: AudioOutput {
id: output
volume: 0.4
}
onErrorOccurred: function(error, errorString) {
const index = currentIndex
currentIndex = null
currentIndex = index;
}
}
Timer {
interval: 10000
repeat: true
running: true
triggeredOnStart: true
onTriggered: function() {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
parent.failedConnAttempts = 0;
// try {
const object = JSON.parse(xhr.responseText.toString()).icestats;
object.source.forEach(station => {
const index = parent.slugLookup[station.server_name];
if (index == null) {
console.warn("Unknown slug encountered in metadata: " + station.server_name);
return
}
parent.streams[index].title = station.title;
parent.streams[index].listeners = station.listeners;
if (index == parent.currentIndex) {
parent.currentStream.title = station.title;
parent.currentStream.listeners = station.listeners;
}
});
// } catch {
// console.error("Failed deserializing metadata response");
// }
}
}
xhr.open("GET", parent.metadataURL);
xhr.timeout = 10000;
xhr.ontimeout = function() {
console.log("Metadata request timed out after 10 seconds");
parent.failedConnAttempts++;
}
xhr.send();
}
}
}

View File

@@ -23,22 +23,22 @@ Item {
primaryAlt: "#0056b3",
secondary: "#009eff",
secondaryAlt: "#0076b3",
surface1: "#323232",
surface0: "#282828",
surface1: "#373737",
surface0: "#2a2a2a",
background: "#1f1f1f"
})
}
readonly property string fontFamily: "Arial"
readonly property var fontSize: ({
h1: 32,
h1: 26,
h2: 24,
h3: 18,
h4: 16,
h5: 13,
h6: 10,
p: 14,
base: 14,
h5: 12,
// h6: 10,
p: 12,
base: 12,
button: 12
})

View File

@@ -20,14 +20,14 @@ ColumnLayout {
Label {
Layout.fillWidth: true
text: "Title: "
text: "Title: " + (index != null ? Player.streams[index].title : "")
heading: "h3"
horizontalAlignment: Text.AlignHCenter
}
Label {
Layout.fillWidth: true
text: "Listeners: "
text: "Listeners: " + (index != null ? Player.streams[index].listeners : 0)
heading: "h3"
horizontalAlignment: Text.AlignHCenter
}
@@ -62,7 +62,7 @@ ColumnLayout {
Layout.leftMargin: 5
from: 0.1
to: 1.1
stepSize: 0.1
stepSize: 0.05
value: Player.volume + 0.1
onMoved: Player.volume = value - 0.1
}