forked from ghostfox/qyouradio
@@ -72,7 +72,7 @@ jobs:
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: '==3.1.*'
|
||||
version: '6.8.0'
|
||||
version: '6.10.0'
|
||||
host: 'windows'
|
||||
target: 'desktop'
|
||||
arch: 'win64_msvc2022_64'
|
||||
|
||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -8,7 +8,7 @@
|
||||
"name": "(gdb) Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/appqyouvideo",
|
||||
"program": "${workspaceFolder}/build/appqyouradio",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/build",
|
||||
|
||||
122
Player.qml.prev
Normal file
122
Player.qml.prev
Normal file
@@ -0,0 +1,122 @@
|
||||
import QtQuick 6.8
|
||||
import QtMultimedia 6.8
|
||||
|
||||
pragma Singleton
|
||||
|
||||
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",
|
||||
slug: "autoradio",
|
||||
title: "",
|
||||
listeners: 0
|
||||
},
|
||||
{
|
||||
name: "Live Mix",
|
||||
slug: "live",
|
||||
title: "",
|
||||
listeners: 0
|
||||
},
|
||||
{
|
||||
name: "Deep Bass",
|
||||
slug: "bassboosted",
|
||||
title: "",
|
||||
listeners: 0
|
||||
}
|
||||
])
|
||||
property var failedConnAttempts: 0
|
||||
|
||||
property alias playing: player.playing
|
||||
property alias volume: output.volume
|
||||
property var loading: player.mediaStatus == Qt.LoadingMedia
|
||||
|
||||
property var currentIndex: null
|
||||
property var currentStream: null
|
||||
|
||||
function startPlaying(index) {
|
||||
if ((playing && index == currentIndex) || index < 0 || index >= streams.length) {
|
||||
return;
|
||||
}
|
||||
if (playing) {
|
||||
player.stop();
|
||||
}
|
||||
print("Starting playing stream no. " + index);
|
||||
currentIndex = index;
|
||||
currentStream = streams[index];
|
||||
player.source = streamURLPrefix + currentStream.slug;
|
||||
player.play();
|
||||
}
|
||||
|
||||
function stopPlaying() {
|
||||
if (!playing) {
|
||||
return;
|
||||
}
|
||||
print("Stopping playback");
|
||||
currentIndex = null;
|
||||
currentStream = null;
|
||||
player.source = "";
|
||||
player.stop()
|
||||
}
|
||||
|
||||
MediaPlayer {
|
||||
id: player
|
||||
source: ""
|
||||
|
||||
audioOutput: AudioOutput {
|
||||
id: output
|
||||
volume: 0.3
|
||||
}
|
||||
|
||||
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.replace(/\[[a-zA-Z0-9]{11}\]/, "");
|
||||
parent.streams[index].listeners = station.listeners;
|
||||
if (index == parent.currentIndex) {
|
||||
parent.currentStream = parent.streams[index];
|
||||
}
|
||||
});
|
||||
// } 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
51
ViewPlayer.qml
Normal file
51
ViewPlayer.qml
Normal file
@@ -0,0 +1,51 @@
|
||||
import QtQuick 6.8
|
||||
import QtQuick.Controls 6.8
|
||||
import QtQuick.Controls.Basic 6.8
|
||||
import QtQuick.Layouts 6.8
|
||||
import QYRComponents 1.0
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
property var index: null
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 20
|
||||
text: qsTr(Player.stations[index].name)
|
||||
font.bold: true
|
||||
heading: "h2"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
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
|
||||
}
|
||||
}
|
||||
180
ViewSettings.qml
Normal file
180
ViewSettings.qml
Normal file
@@ -0,0 +1,180 @@
|
||||
import QtQuick 6.8
|
||||
import QtQuick.Controls 6.8
|
||||
import QtQuick.Controls.Basic 6.8
|
||||
import QtQuick.Layouts 6.8
|
||||
import QYRComponents 1.0
|
||||
|
||||
ApplicationWindow {
|
||||
width: 840
|
||||
height: 560
|
||||
visible: true
|
||||
title: qsTr("Settings")
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Container {
|
||||
id: settingsCategory
|
||||
Layout.fillWidth: false
|
||||
Layout.fillHeight: true
|
||||
Layout.rightMargin: 15
|
||||
|
||||
implicitWidth: 240
|
||||
clip: true
|
||||
|
||||
contentItem: ListView {
|
||||
spacing: 7.5
|
||||
model: settingsCategory.contentModel
|
||||
snapMode: ListView.SnapOneItem
|
||||
orientation: ListView.Vertical
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
text: qsTr("Appearance")
|
||||
width: 240
|
||||
outlined: true
|
||||
onClicked: settingsCategory.currentIndex = 0
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Language")
|
||||
width: 240
|
||||
outlined: true
|
||||
onClicked: settingsCategory.currentIndex = 1
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("Playback Settings")
|
||||
width: 240
|
||||
outlined: true
|
||||
onClicked: settingsCategory.currentIndex = 2
|
||||
}
|
||||
|
||||
Button {
|
||||
text: qsTr("About")
|
||||
width: 240
|
||||
outlined: true
|
||||
onClicked: settingsCategory.currentIndex = 3
|
||||
}
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
orientation: Qt.Vertical
|
||||
interactive: true
|
||||
currentIndex: tabbar.currentIndex
|
||||
|
||||
Loader {
|
||||
// active: tabbar.currentIndex == 0
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
sourceComponent: ColumnLayout {
|
||||
Label {
|
||||
text: "Appearance"
|
||||
heading: "h1"
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
// active: tabbar.currentIndex == 1
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
sourceComponent: ColumnLayout {
|
||||
Label {
|
||||
text: "Language"
|
||||
heading: "h1"
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
// active: tabbar.currentIndex == 2
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
sourceComponent: ColumnLayout {
|
||||
Label {
|
||||
text: "Playback Settings"
|
||||
heading: "h1"
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
// active: tabbar.currentIndex == 3
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
sourceComponent: ColumnLayout {
|
||||
Label {
|
||||
text: "About"
|
||||
heading: "h1"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "YouRadio"
|
||||
heading: "h2"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "by Youpiter"
|
||||
heading: "h3"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Music source"
|
||||
heading: "base"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "QYouRadio"
|
||||
heading: "h3"
|
||||
font.bold: true
|
||||
}
|
||||
Label {
|
||||
text: "by Ghostfox"
|
||||
heading: "h3"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Client development"
|
||||
heading: "base"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Attribution"
|
||||
heading: "h1"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Qt"
|
||||
heading: "h2"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "by Qt Group Inc."
|
||||
heading: "h3"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Open-Source library on which QYouRadio is built uppon, licensed under LGPL-3.0"
|
||||
heading: "base"
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
buildFlags.h.in
Normal file
1
buildFlags.h.in
Normal file
@@ -0,0 +1 @@
|
||||
#cmakedefine01 DiscordSDK_FOUND
|
||||
103
cmake/FindDiscordSDK.cmake
Normal file
103
cmake/FindDiscordSDK.cmake
Normal file
@@ -0,0 +1,103 @@
|
||||
# Locate Discord Social SDK library and headers.
|
||||
#
|
||||
# Copyright (c) 2025 hat_kid
|
||||
# https://github.com/thehatkid/DiscordSocialSDKExample
|
||||
#
|
||||
# Usage of this module as follows:
|
||||
# find_package(DiscordSDK)
|
||||
#
|
||||
# Variables defined by this module:
|
||||
# DISCORDSDK_FOUND Whether was found library and headers.
|
||||
# DISCORDSDK_INCLUDE_DIR SDK include path.
|
||||
# DISCORDSDK_LIBRARY SDK shared library path.
|
||||
# DISCORDSDK_IMPLIB Win32: SDK object library path to link.
|
||||
|
||||
# Set SDK root directory path (You can change it to different path)
|
||||
set(DISCORDSDK_ROOT_DIR "${CMAKE_SOURCE_DIR}/ext/discord_social_sdk" CACHE PATH "Discord Social SDK root path")
|
||||
|
||||
# Set SDK library build variant
|
||||
set(DISCORDSDK_VARIANT "release" CACHE STRING "Discord Social SDK library variant")
|
||||
|
||||
set_property(CACHE DISCORDSDK_VARIANT PROPERTY STRINGS "release" "debug")
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(DISCORDSDK_VARIANT "debug")
|
||||
endif()
|
||||
|
||||
# Find SDK include directory path
|
||||
find_path(
|
||||
DISCORDSDK_INCLUDE_DIR
|
||||
NAMES cdiscord.h discordpp.h
|
||||
PATHS ${DISCORDSDK_ROOT_DIR}/include
|
||||
DOC "Discord Social SDK include directory"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
# Find SDK library path
|
||||
if(WIN32)
|
||||
find_file(
|
||||
DISCORDSDK_LIBRARY
|
||||
NAMES discord_partner_sdk.dll
|
||||
PATHS ${DISCORDSDK_ROOT_DIR}/bin/${DISCORDSDK_VARIANT}
|
||||
DOC "Discord Social SDK Windows Dynamic Link Library (.dll)"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
find_file(
|
||||
DISCORDSDK_IMPLIB
|
||||
NAMES discord_partner_sdk.lib
|
||||
PATHS ${DISCORDSDK_ROOT_DIR}/lib/${DISCORDSDK_VARIANT}
|
||||
DOC "Discord Social SDK Windows Object Library (.lib)"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
else()
|
||||
find_library(
|
||||
DISCORDSDK_LIBRARY
|
||||
NAMES libdiscord_partner_sdk discord_partner_sdk
|
||||
PATHS ${DISCORDSDK_ROOT_DIR}/lib/${DISCORDSDK_VARIANT}
|
||||
DOC "Discord Social SDK shared library"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
DISCORDSDK_ROOT_DIR
|
||||
DISCORDSDK_VARIANT
|
||||
DISCORDSDK_INCLUDE_DIR
|
||||
DISCORDSDK_LIBRARY
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
if(WIN32)
|
||||
find_package_handle_standard_args(
|
||||
DiscordSDK
|
||||
REQUIRED_VARS DISCORDSDK_IMPLIB DISCORDSDK_LIBRARY DISCORDSDK_INCLUDE_DIR
|
||||
)
|
||||
mark_as_advanced(DISCORDSDK_IMPLIB)
|
||||
else()
|
||||
find_package_handle_standard_args(
|
||||
DiscordSDK
|
||||
REQUIRED_VARS DISCORDSDK_LIBRARY DISCORDSDK_INCLUDE_DIR
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT DiscordSDK_FOUND)
|
||||
message("Could NOT find Discord Social SDK redistributable! Please check for SDK files in ${DISCORDSDK_ROOT_DIR}")
|
||||
else()
|
||||
# Add imported shared library as DiscordSDK::DiscordSDK
|
||||
add_library(DiscordSDK::DiscordSDK SHARED IMPORTED)
|
||||
|
||||
set_target_properties(
|
||||
DiscordSDK::DiscordSDK PROPERTIES
|
||||
IMPORTED_LOCATION ${DISCORDSDK_LIBRARY}
|
||||
INTERFACE_COMPILE_DEFINITIONS DISCORDPP_IMPLEMENTATION
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${DISCORDSDK_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(
|
||||
DiscordSDK::DiscordSDK PROPERTIES
|
||||
IMPORTED_IMPLIB ${DISCORDSDK_IMPLIB}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
0
ext/.gitkeep
Normal file
0
ext/.gitkeep
Normal file
Reference in New Issue
Block a user