Begin implementing UI design, implement metadata fetching
This commit is contained in:
136
Main.qml
136
Main.qml
@@ -13,80 +13,96 @@ ApplicationWindow {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 10
|
|
||||||
|
|
||||||
RowLayout {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
height: 43
|
||||||
|
|
||||||
Label {
|
color: Colors.surface0
|
||||||
text: "QYouRadio"
|
|
||||||
heading: "h3"
|
|
||||||
font.bold: true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Item {
|
RowLayout {
|
||||||
// Layout.fillWidth: true
|
anchors.fill: parent
|
||||||
// }
|
anchors.margins: 3
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.leftMargin: 20
|
|
||||||
visible: Player.currentIndex != null
|
|
||||||
color: Colors.primary
|
|
||||||
radius: 5
|
|
||||||
clip: true
|
|
||||||
width: 320
|
|
||||||
height: 36
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
anchors.top: parent.top
|
Layout.leftMargin: 5
|
||||||
anchors.left: parent.left
|
text: "QYouRadio"
|
||||||
anchors.right: parent.right
|
heading: "h1"
|
||||||
anchors.topMargin: 1
|
// font.bold: true
|
||||||
anchors.leftMargin: 4
|
|
||||||
anchors.rightMargin: 4
|
|
||||||
text: (Player.loading ? "Loading " : "Playing ") + (Player.currentIndex != null ? qsTr(Player.currentStream.name) : "")
|
|
||||||
heading: "h2"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Rectangle {
|
||||||
anchors.bottom: parent.bottom
|
Layout.leftMargin: 5
|
||||||
anchors.left: parent.left
|
visible: Player.currentIndex != null
|
||||||
anchors.right: parent.right
|
color: Colors.primary
|
||||||
anchors.bottomMargin: 1
|
radius: 5
|
||||||
anchors.leftMargin: 4
|
clip: true
|
||||||
anchors.rightMargin: 4
|
width: 320
|
||||||
text: "Title will go here"
|
height: 36
|
||||||
heading: "h4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
Label {
|
||||||
Layout.fillWidth: true
|
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 {
|
Label {
|
||||||
id: tabbar
|
id: nowplaying_title
|
||||||
spacing: 10
|
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 {
|
NumberAnimation { target: nowplaying_title; property: "anchors.rightMargin"; from: 0; to: 320; duration: 5000 }
|
||||||
text: qsTr("Autoradio")
|
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")
|
TabBar {
|
||||||
}
|
id: tabbar
|
||||||
}
|
spacing: 10
|
||||||
Button {
|
|
||||||
text: "S"
|
background: Item{}
|
||||||
onClicked: function() {
|
|
||||||
var component = Qt.createComponent("ViewSettings.qml")
|
TabButton {
|
||||||
var window = component.createObject(root)
|
text: qsTr("Autoradio")
|
||||||
window.show()
|
}
|
||||||
|
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 {
|
SwipeView {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.margins: 10
|
Layout.margins: 20
|
||||||
|
|
||||||
interactive: false
|
interactive: false
|
||||||
currentIndex: tabbar.currentIndex
|
currentIndex: tabbar.currentIndex
|
||||||
|
|||||||
101
Player.qml
101
Player.qml
@@ -3,38 +3,39 @@ import QtMultimedia 6.8
|
|||||||
|
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
|
|
||||||
MediaPlayer {
|
Item {
|
||||||
readonly property var streams: ([
|
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",
|
name: "Autoradio",
|
||||||
streamURL: "https://youradio.nonamesoft.xyz/api/autoradio"
|
slug: "autoradio",
|
||||||
|
title: "",
|
||||||
|
listeners: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Live Mix",
|
name: "Live Mix",
|
||||||
streamURL: "https://youradio.nonamesoft.xyz/api/live"
|
slug: "live",
|
||||||
|
title: "",
|
||||||
|
listeners: 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Deep Bass",
|
name: "Deep Bass",
|
||||||
streamURL: "https://youradio.nonamesoft.xyz/api/bassboosted"
|
slug: "bassboosted",
|
||||||
|
title: "",
|
||||||
|
listeners: 0
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
property var failedConnAttempts: 0
|
||||||
|
|
||||||
id: player
|
property alias playing: player.playing
|
||||||
source: ""
|
|
||||||
|
|
||||||
audioOutput: AudioOutput {
|
|
||||||
id: output
|
|
||||||
volume: 0.4
|
|
||||||
}
|
|
||||||
|
|
||||||
onErrorOccurred: function(error, errorString) {
|
|
||||||
const index = currentIndex
|
|
||||||
currentIndex = null
|
|
||||||
currentIndex = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
property alias volume: output.volume
|
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 currentIndex: null
|
||||||
property var currentStream: null
|
property var currentStream: null
|
||||||
@@ -49,7 +50,7 @@ MediaPlayer {
|
|||||||
print("Starting playing stream no. " + index);
|
print("Starting playing stream no. " + index);
|
||||||
currentIndex = index;
|
currentIndex = index;
|
||||||
currentStream = streams[index];
|
currentStream = streams[index];
|
||||||
source = currentStream.streamURL;
|
player.source = streamURLPrefix + currentStream.slug;
|
||||||
player.play();
|
player.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +61,63 @@ MediaPlayer {
|
|||||||
print("Stopping playback");
|
print("Stopping playback");
|
||||||
currentIndex = null;
|
currentIndex = null;
|
||||||
currentStream = null;
|
currentStream = null;
|
||||||
source = "";
|
player.source = "";
|
||||||
player.stop()
|
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",
|
primaryAlt: "#0056b3",
|
||||||
secondary: "#009eff",
|
secondary: "#009eff",
|
||||||
secondaryAlt: "#0076b3",
|
secondaryAlt: "#0076b3",
|
||||||
surface1: "#323232",
|
surface1: "#373737",
|
||||||
surface0: "#282828",
|
surface0: "#2a2a2a",
|
||||||
background: "#1f1f1f"
|
background: "#1f1f1f"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property string fontFamily: "Arial"
|
readonly property string fontFamily: "Arial"
|
||||||
readonly property var fontSize: ({
|
readonly property var fontSize: ({
|
||||||
h1: 32,
|
h1: 26,
|
||||||
h2: 24,
|
h2: 24,
|
||||||
h3: 18,
|
h3: 18,
|
||||||
h4: 16,
|
h4: 16,
|
||||||
h5: 13,
|
h5: 12,
|
||||||
h6: 10,
|
// h6: 10,
|
||||||
p: 14,
|
p: 12,
|
||||||
base: 14,
|
base: 12,
|
||||||
button: 12
|
button: 12
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ ColumnLayout {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: "Title: "
|
text: "Title: " + (index != null ? Player.streams[index].title : "")
|
||||||
heading: "h3"
|
heading: "h3"
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: "Listeners: "
|
text: "Listeners: " + (index != null ? Player.streams[index].listeners : 0)
|
||||||
heading: "h3"
|
heading: "h3"
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ ColumnLayout {
|
|||||||
Layout.leftMargin: 5
|
Layout.leftMargin: 5
|
||||||
from: 0.1
|
from: 0.1
|
||||||
to: 1.1
|
to: 1.1
|
||||||
stepSize: 0.1
|
stepSize: 0.05
|
||||||
value: Player.volume + 0.1
|
value: Player.volume + 0.1
|
||||||
onMoved: Player.volume = value - 0.1
|
onMoved: Player.volume = value - 0.1
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user