Begin implementing UI design, implement metadata fetching
This commit is contained in:
66
Main.qml
66
Main.qml
@@ -13,23 +13,26 @@ ApplicationWindow {
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Label {
|
||||
text: "QYouRadio"
|
||||
heading: "h3"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
// Item {
|
||||
// Layout.fillWidth: true
|
||||
// }
|
||||
|
||||
Rectangle {
|
||||
Layout.leftMargin: 20
|
||||
Layout.fillWidth: true
|
||||
height: 43
|
||||
|
||||
color: Colors.surface0
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 3
|
||||
|
||||
Label {
|
||||
Layout.leftMargin: 5
|
||||
text: "QYouRadio"
|
||||
heading: "h1"
|
||||
// font.bold: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.leftMargin: 5
|
||||
visible: Player.currentIndex != null
|
||||
color: Colors.primary
|
||||
radius: 5
|
||||
@@ -45,18 +48,27 @@ ApplicationWindow {
|
||||
anchors.leftMargin: 4
|
||||
anchors.rightMargin: 4
|
||||
text: (Player.loading ? "Loading " : "Playing ") + (Player.currentIndex != null ? qsTr(Player.currentStream.name) : "")
|
||||
heading: "h2"
|
||||
heading: "h5"
|
||||
}
|
||||
|
||||
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: "Title will go here"
|
||||
heading: "h4"
|
||||
text: (Player.currentIndex != null ? qsTr(Player.currentStream.title) : "")
|
||||
heading: "h3"
|
||||
|
||||
SequentialAnimation {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
|
||||
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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,14 +91,18 @@ ApplicationWindow {
|
||||
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()
|
||||
}
|
||||
// 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
|
||||
|
||||
101
Player.qml
101
Player.qml
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user