forked from ghostfox/qyouradio
fist comin
This commit is contained in:
@@ -1,49 +1,42 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
project(qyouradio VERSION 0.1 LANGUAGES CXX)
|
||||
project(qyouvideo VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Quick Multimedia)
|
||||
find_package(DiscordSDK)
|
||||
|
||||
qt_standard_project_setup(REQUIRES 6.8)
|
||||
|
||||
configure_file(buildFlags.h.in buildFlags.h)
|
||||
|
||||
qt_add_executable(appqyouradio
|
||||
qt_add_executable(appqyouvideo
|
||||
main.cpp
|
||||
buildFlags.h
|
||||
player.cpp player.h
|
||||
resources/qyouradio.rc
|
||||
resources/qyouvideo.rc
|
||||
resources/resource.h
|
||||
)
|
||||
|
||||
qt_add_resources(appqyouradio "resources"
|
||||
qt_add_resources(appqyouvideo "resources"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
resources/logo.png
|
||||
)
|
||||
|
||||
set_source_files_properties(Player.qml
|
||||
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
|
||||
|
||||
qt_add_qml_module(appqyouradio
|
||||
URI qyouradio
|
||||
qt_add_qml_module(appqyouvideo
|
||||
URI qyouvideo
|
||||
VERSION 1.0
|
||||
QML_FILES
|
||||
Main.qml
|
||||
# Player.qml
|
||||
ViewPlayer.qml
|
||||
ViewSettings.qml
|
||||
VideoEntry.qml
|
||||
ViewAbout.qml
|
||||
ViewVideoList.qml
|
||||
VideoPlayer.qml
|
||||
)
|
||||
|
||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||
# If you are developing for iOS or macOS you should consider setting an
|
||||
# explicit, fixed bundle identifier manually though.
|
||||
set_target_properties(appqyouradio PROPERTIES
|
||||
set_target_properties(appqyouvideo PROPERTIES
|
||||
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appqyouradio
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||
@@ -51,30 +44,25 @@ set_target_properties(appqyouradio PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
)
|
||||
|
||||
target_include_directories(appqyouradio PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
target_link_libraries(appqyouradio
|
||||
target_link_libraries(appqyouvideo
|
||||
PUBLIC Qt6::Quick Qt6::Multimedia
|
||||
)
|
||||
|
||||
if (DiscordSDK_FOUND)
|
||||
target_link_libraries(appqyouradio
|
||||
PRIVATE DiscordSDK::DiscordSDK
|
||||
)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS appqyouradio
|
||||
install(TARGETS appqyouvideo
|
||||
BUNDLE DESTINATION .
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
qt_generate_deploy_app_script(
|
||||
TARGET appqyouradio
|
||||
TARGET appqyouvideo
|
||||
OUTPUT_SCRIPT deploy_script
|
||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||
)
|
||||
install(SCRIPT ${deploy_script})
|
||||
|
||||
add_subdirectory(QYRComponents)
|
||||
|
||||
project(qyouvideo VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
134
Main.qml
134
Main.qml
@@ -9,11 +9,7 @@ ApplicationWindow {
|
||||
id: root
|
||||
width: 1280
|
||||
height: 800
|
||||
title: qsTr("QYouRadio")
|
||||
|
||||
Component.onCompleted: function() {
|
||||
console.log(Player.stations)
|
||||
}
|
||||
title: qsTr("QYouVideo")
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
@@ -30,76 +26,8 @@ ApplicationWindow {
|
||||
|
||||
Label {
|
||||
Layout.leftMargin: 5
|
||||
text: "QYouRadio"
|
||||
text: "QYouVideo"
|
||||
heading: "h1"
|
||||
// font.bold: true
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.leftMargin: 5
|
||||
width: 320
|
||||
height: 36
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
id: nowplaying_root
|
||||
// visible: Player.currentIndex != null
|
||||
visible: anchors.bottomMargin < 42
|
||||
color: Colors.primary
|
||||
radius: 5
|
||||
clip: true
|
||||
|
||||
NumberAnimation {
|
||||
running: Player.playing && tabbar.currentIndex != Player.currentIndex && nowplaying_root.anchors.bottomMargin >= 42
|
||||
target: nowplaying_root
|
||||
property: "anchors.bottomMargin"
|
||||
from: 42
|
||||
to: 0
|
||||
duration: 145
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
running: !Player.playing || (tabbar.currentIndex == Player.currentIndex && nowplaying_root.anchors.bottomMargin < 42)
|
||||
target: nowplaying_root
|
||||
property: "anchors.bottomMargin"
|
||||
from: 0
|
||||
to: 42
|
||||
duration: 145
|
||||
}
|
||||
|
||||
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 ") + qsTr(Player.currentStation.name)
|
||||
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: qsTr(Player.currentStation.songTitle)
|
||||
heading: "h3"
|
||||
|
||||
|
||||
SequentialAnimation {
|
||||
running: nowplaying_title.width > 320
|
||||
loops: Animation.Infinite
|
||||
|
||||
NumberAnimation { target: nowplaying_title; property: "anchors.leftMargin"; from: 4; to: 4; duration: 10000 }
|
||||
NumberAnimation { target: nowplaying_title; property: "anchors.leftMargin"; from: 4; to: -nowplaying_title.width + 324; duration: nowplaying_title.width * 4 }
|
||||
NumberAnimation { target: nowplaying_title; property: "anchors.leftMargin"; from: -nowplaying_title.width + 324; to: -nowplaying_title.width + 324; duration: 10000 }
|
||||
NumberAnimation { target: nowplaying_title; property: "anchors.leftMargin"; from: -nowplaying_title.width + 324; to: 4; duration: nowplaying_title.width * 4 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -113,16 +41,17 @@ ApplicationWindow {
|
||||
background: Item{}
|
||||
|
||||
TabButton {
|
||||
text: qsTr("Autoradio")
|
||||
text: qsTr("Videos")
|
||||
outlined: true
|
||||
implicitWidth: 80
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Live Mix")
|
||||
text: qsTr("About")
|
||||
outlined: true
|
||||
implicitWidth: 80
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Deep Bass")
|
||||
}
|
||||
TabButton {
|
||||
text: qsTr("Settings")
|
||||
text: qsTr("Upload")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,61 +70,28 @@ ApplicationWindow {
|
||||
active: true
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
sourceComponent: ViewPlayer {
|
||||
index: 0
|
||||
}
|
||||
source: "ViewVideoList.qml"
|
||||
}
|
||||
Loader {
|
||||
// active: tabbar.currentIndex == 1
|
||||
active: true
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
sourceComponent: ViewPlayer {
|
||||
index: 1
|
||||
}
|
||||
source: "ViewAbout.qml"
|
||||
}
|
||||
Loader {
|
||||
// active: tabbar.currentIndex == 2
|
||||
active: true
|
||||
asynchronous: true
|
||||
visible: status == Loader.Ready
|
||||
sourceComponent: ViewPlayer {
|
||||
index: 2
|
||||
sourceComponent: Label {
|
||||
heading: "h1"
|
||||
text: "No idea if I'll ever implement this so don't worry >w<"
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: contentItem.highlightMoveDuration = 160;
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: false
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
80
VideoEntry.qml
Normal file
80
VideoEntry.qml
Normal file
@@ -0,0 +1,80 @@
|
||||
import QtQuick 6.8
|
||||
import QtQuick.Controls 6.8
|
||||
import QtQuick.Layouts 6.8
|
||||
|
||||
import QYRComponents 1.0
|
||||
|
||||
Rectangle {
|
||||
anchors.margins: 10
|
||||
|
||||
width: 384
|
||||
height: 224
|
||||
color: area.containsMouse ? Colors.surface1 : Colors.surface0
|
||||
radius: 10
|
||||
|
||||
required property string name
|
||||
required property string id
|
||||
required property var metadata
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
|
||||
Image {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
asynchronous: true
|
||||
cache: true
|
||||
source: "https://youvideo.nonamesoft.xyz/thumbnails/" + parent.parent.id
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
onStatusChanged: if (status == Image.Error) {
|
||||
source = "https://youvideo.nonamesoft.xyz/thumbnails/audio.png";
|
||||
children[0].visible = true
|
||||
}
|
||||
|
||||
// This is a fucking mess
|
||||
Label {
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
heading: "h3"
|
||||
font.bold: true
|
||||
text: parent.parent.parent.name
|
||||
wrapMode: Text.Wrap
|
||||
clip: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
color: Colors.background
|
||||
|
||||
width: children[0].paintedWidth
|
||||
height: children[0].paintedHeight
|
||||
|
||||
Label {
|
||||
anchors.fill: parent
|
||||
heading: "h4"
|
||||
// text: new Date(parent.parent.parent.parent.metadata.duration * 1000).toISOString().slice(11, 19)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h3"
|
||||
font.bold: true
|
||||
text: parent.parent.name
|
||||
Layout.fillWidth: true
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
id: area
|
||||
hoverEnabled: true
|
||||
onClicked: Player.loadVideo(parent.id)
|
||||
}
|
||||
}
|
||||
49
VideoPlayer.qml
Normal file
49
VideoPlayer.qml
Normal file
@@ -0,0 +1,49 @@
|
||||
import QtQuick 6.8
|
||||
import QtMultimedia 6.8
|
||||
import QtQuick.Controls 6.8
|
||||
import QtQuick.Layouts 6.8
|
||||
|
||||
import QYRComponents 1.0
|
||||
|
||||
Rectangle {
|
||||
visible: Player.hasVideo
|
||||
color: Colors.surface1
|
||||
|
||||
width: childrenRect.width
|
||||
height: childrenRect.height
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
RowLayout {
|
||||
Button {
|
||||
text: Player.playing ? "Pause" : "Play"
|
||||
onClicked: function() {
|
||||
if (Player.playing) {
|
||||
Player.pause()
|
||||
} else {
|
||||
Player.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Stop"
|
||||
onClicked: Player.stop()
|
||||
}
|
||||
|
||||
Slider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 106
|
||||
}
|
||||
|
||||
Slider {
|
||||
from: 0
|
||||
to: 1
|
||||
value: Player.volume
|
||||
onMoved: Player.volume = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
ViewAbout.qml
Normal file
59
ViewAbout.qml
Normal file
@@ -0,0 +1,59 @@
|
||||
import QtQuick 6.8
|
||||
import QtQuick.Controls 6.8
|
||||
import QtQuick.Layouts 6.8
|
||||
|
||||
import QYRComponents 1.0
|
||||
|
||||
ColumnLayout {
|
||||
Label {
|
||||
heading: "h2"
|
||||
text: "About"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h3"
|
||||
text: "Youpiter"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h4"
|
||||
text: "Creator of YouVideo"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h3"
|
||||
text: "Ghostfox"
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h4"
|
||||
text: "Creator of QYouRadio"
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h3"
|
||||
text: "Ghostfox"
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h4"
|
||||
text: "Creator of QYouVideo"
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h3"
|
||||
text: "Qt Group"
|
||||
}
|
||||
|
||||
Label {
|
||||
heading: "h4"
|
||||
text: "Creator of Qt, QML and QtQuick"
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
85
ViewVideoList.qml
Normal file
85
ViewVideoList.qml
Normal file
@@ -0,0 +1,85 @@
|
||||
import QtQuick 6.8
|
||||
import QtQuick.Controls 6.8
|
||||
import QtQuick.Layouts 6.8
|
||||
|
||||
import QYRComponents 1.0
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
property bool loading: true
|
||||
property bool failed: false
|
||||
|
||||
VideoPlayer {}
|
||||
|
||||
GridView {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: !loading || !failed
|
||||
anchors.fill: parent
|
||||
model: ListModel { id: model }
|
||||
clip: true
|
||||
|
||||
cellWidth: 384
|
||||
cellHeight: 224
|
||||
|
||||
delegate: VideoEntry {}
|
||||
|
||||
Component.onCompleted: parent.fetchData()
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
visible: loading
|
||||
running: loading
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
anchors.centerIn: parent
|
||||
visible: failed
|
||||
|
||||
Label {
|
||||
text: "Couldn't get videos!"
|
||||
heading: "h2"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "This could mean that either your internet or YouVideo is down."
|
||||
heading: "h4"
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.bottomMargin: 25
|
||||
text: "Check your network connection and try again!"
|
||||
heading: "h4"
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Retry"
|
||||
onClicked: parent.fetchData()
|
||||
}
|
||||
}
|
||||
|
||||
function fetchData() {
|
||||
model.clear();
|
||||
loading = true;
|
||||
failed = false;
|
||||
const xhr = new XMLHttpRequest;
|
||||
xhr.open("GET", "http://youvideo.nonamesoft.xyz/youvideo/api/videos");
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == XMLHttpRequest.DONE) {
|
||||
loading = false;
|
||||
if (xhr.status != 200) {
|
||||
failed = true;
|
||||
return;
|
||||
}
|
||||
const data = JSON.parse(xhr.responseText);
|
||||
data.forEach(video => {
|
||||
model.append(video);
|
||||
});
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
, wrapQtAppsHook
|
||||
}:
|
||||
stdenv.mkDerivation {
|
||||
pname = "qyouradio";
|
||||
pname = "qyouvideo";
|
||||
version = "1.0";
|
||||
|
||||
src = ./.;
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1760315168,
|
||||
"narHash": "sha256-qWDhFoiz6VSd+S+rVzC2m5u8xuAzxRsuDI8OojbPEZ4=",
|
||||
"lastModified": 1760807995,
|
||||
"narHash": "sha256-Xpg9h3/uNVMJXZq2LzBDaDM1u057ox0wnZGvxSogNY0=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "160e3dfce302bb2fcd03761b84248c7534f3b948",
|
||||
"rev": "a2975bcaf36af01a1279b84ab575fbc12d472b6a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
11
main.cpp
11
main.cpp
@@ -1,9 +1,6 @@
|
||||
#include <QIcon>
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QDebug>
|
||||
|
||||
#include "buildFlags.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@@ -13,15 +10,9 @@ int main(int argc, char *argv[])
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
|
||||
&app, []() { QCoreApplication::exit(-1); },
|
||||
Qt::QueuedConnection);
|
||||
engine.loadFromModule("qyouradio", "Main");
|
||||
engine.loadFromModule("qyouvideo", "Main");
|
||||
|
||||
app.setWindowIcon(QIcon(":/resources/logo.png"));
|
||||
|
||||
#if DiscordSDK_FOUND
|
||||
qInfo("Has Discord Social SDK: true");
|
||||
#else
|
||||
qInfo("Has Discord Social SDK: false");
|
||||
#endif
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
131
player.cpp
131
player.cpp
@@ -2,32 +2,21 @@
|
||||
|
||||
// Public
|
||||
Player::Player(QObject *parent) : QObject(parent),
|
||||
player(this), output(this), timer(this), m_currentIndex(0), m_stations({
|
||||
Station{"Autoradio", "autoradio", "", 0},
|
||||
Station{"Live Mix", "live", "", 0},
|
||||
Station{"Bass Boosted", "bassboosted", "", 0}
|
||||
})
|
||||
player(this), output(this), manager(this)
|
||||
{
|
||||
QObject::connect(&this->player, &QMediaPlayer::mediaStatusChanged, this, [this] () {
|
||||
emit this->hasVideoChanged();
|
||||
emit this->loadingChanged();
|
||||
});
|
||||
|
||||
QObject::connect(&this->player, &QMediaPlayer::playbackStateChanged, this, [this] () {
|
||||
qDebug("Player::Player()->Lambda: Playing got changed!");
|
||||
emit this->playingChanged();
|
||||
});
|
||||
|
||||
QObject::connect(&this->output, &QAudioOutput::volumeChanged, this, [this] () {
|
||||
qDebug("Player::Player()->Lambda: Volume got changed!");
|
||||
emit this->volumeChanged();
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
@@ -39,12 +28,26 @@ Player::~Player()
|
||||
qDebug("Player::~Player(): Destructed");
|
||||
}
|
||||
|
||||
bool Player::hasVideo() const
|
||||
{
|
||||
return this->player.isAvailable();
|
||||
}
|
||||
|
||||
bool Player::loading() const
|
||||
{
|
||||
return this->player.mediaStatus() == QMediaPlayer::LoadingMedia || this->player.mediaStatus() == QMediaPlayer::BufferingMedia;
|
||||
}
|
||||
|
||||
bool Player::failed() const
|
||||
{
|
||||
return this->m_failed;
|
||||
}
|
||||
|
||||
bool Player::playing() const
|
||||
{
|
||||
return this->player.playbackState() == QMediaPlayer::PlayingState;
|
||||
}
|
||||
|
||||
|
||||
float Player::volume() const
|
||||
{
|
||||
return this->output.volume();
|
||||
@@ -55,59 +58,67 @@ void Player::setVolume(float 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
|
||||
void Player::startPlaying(u_int8_t index)
|
||||
void Player::loadVideo(QString id)
|
||||
{
|
||||
if (index < 0 || index >= this->m_stations.length())
|
||||
if (this->hasVideo())
|
||||
{
|
||||
qWarning("Player::startPlaying(): Tried accessing out of bounds.");
|
||||
this->unloadVideo();
|
||||
}
|
||||
const QNetworkReply* reply = this->manager.get(QNetworkRequest(QUrl("https://youvideo.nonamesoft.xyz/youvideo/video/" + id)));
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qDebug() << "Player::loadVideo(): Video " << id << " doesn't exist!\n";
|
||||
delete reply;
|
||||
return;
|
||||
}
|
||||
if (this->playing()) {
|
||||
this->stopPlaying();
|
||||
}
|
||||
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();
|
||||
qDebug("Player::startPlaying(): Starting playing");
|
||||
qDebug() << "Player::fetchMetadata(): Yippie! Got video " << id << "\n";
|
||||
|
||||
this->player.setSource(QUrl("https://youvideo.nonamesoft.xyz/youvideo/api/" + id));
|
||||
|
||||
// Signals will be sent by shit sent from player
|
||||
this->m_failed = false;
|
||||
emit this->failedChanged();
|
||||
|
||||
delete reply;
|
||||
}
|
||||
|
||||
void Player::stopPlaying()
|
||||
void Player::unloadVideo() {
|
||||
this->player.setSource(QUrl(""));
|
||||
this->player.stop();
|
||||
this->m_failed = false;
|
||||
|
||||
emit this->hasVideoChanged();
|
||||
emit this->failedChanged();
|
||||
emit this->loadingChanged();
|
||||
emit this->playingChanged();
|
||||
}
|
||||
|
||||
void Player::play()
|
||||
{
|
||||
if (!this->playing())
|
||||
if (!this->hasVideo() || !this->loading())
|
||||
{
|
||||
return;
|
||||
}
|
||||
this->player.play();
|
||||
}
|
||||
|
||||
void Player::pause()
|
||||
{
|
||||
if (!this->hasVideo() || !this->loading())
|
||||
{
|
||||
return;
|
||||
}
|
||||
this->player.pause();
|
||||
}
|
||||
|
||||
void Player::stop()
|
||||
{
|
||||
if (!this->hasVideo() || !this->loading())
|
||||
{
|
||||
qWarning("Player::stopPlaying(): Prevented redundant stopPlaying() call.");
|
||||
return;
|
||||
}
|
||||
this->player.stop();
|
||||
this->player.setSource(QUrl(""));
|
||||
|
||||
this->m_currentIndex = -1;
|
||||
emit this->currentIndexChanged();
|
||||
emit this->currentStationChanged();
|
||||
|
||||
qDebug("Player::stopPlaying(): Stopping playback...");
|
||||
this->unloadVideo();
|
||||
}
|
||||
|
||||
57
player.h
57
player.h
@@ -1,73 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtMultimedia>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QtQmlIntegration/qqmlintegration.h>
|
||||
|
||||
constexpr int metadataFetchTimeout = 10000;
|
||||
constexpr std::string_view metadataEndpoint = "https://youradio.nonamesoft.xyz/youradio/api/status-json.xsl";
|
||||
|
||||
struct Station
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
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);
|
||||
|
||||
public:
|
||||
QString m_name;
|
||||
QString m_slug;
|
||||
QString m_songTitle;
|
||||
int m_listeners;
|
||||
};
|
||||
|
||||
class Player : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_SINGLETON
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged FINAL)
|
||||
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged FINAL)
|
||||
Q_PROPERTY(bool failed READ failed NOTIFY failedChanged FINAL)
|
||||
Q_PROPERTY(bool playing READ playing NOTIFY playingChanged FINAL)
|
||||
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:
|
||||
Player(QObject *parent = nullptr);
|
||||
~Player();
|
||||
|
||||
bool hasVideo() const;
|
||||
bool loading() const;
|
||||
bool failed() const;
|
||||
bool playing() const;
|
||||
|
||||
float volume() const;
|
||||
|
||||
void setVolume(float newVolume);
|
||||
|
||||
const QList<Station> stations() const;
|
||||
|
||||
const int currentIndex() const;
|
||||
const Station* currentStation() const;
|
||||
|
||||
public slots:
|
||||
void startPlaying(u_int8_t index);
|
||||
void stopPlaying();
|
||||
void loadVideo(QString id);
|
||||
void unloadVideo();
|
||||
void play();
|
||||
void pause();
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
void hasVideoChanged();
|
||||
void loadingChanged();
|
||||
void failedChanged();
|
||||
void playingChanged();
|
||||
void volumeChanged();
|
||||
void stationsChanged();
|
||||
void currentIndexChanged();
|
||||
void currentStationChanged();
|
||||
|
||||
private:
|
||||
QMediaPlayer player;
|
||||
QAudioOutput output;
|
||||
QTimer timer;
|
||||
QNetworkAccessManager manager;
|
||||
|
||||
int m_currentIndex;
|
||||
QList<Station> m_stations;
|
||||
bool m_failed = false;
|
||||
};
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
//LANGUAGE 0x09, 0x01
|
||||
#pragma code_page(1252)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 1, 0, 0, 1
|
||||
PRODUCTVERSION 1, 0, 0, 1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x29L
|
||||
#else
|
||||
FILEFLAGS 0x28L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "LAMINAX CO."
|
||||
VALUE "FileDescription", "QYouRadio"
|
||||
VALUE "FileVersion", "1.0.0.1"
|
||||
VALUE "InternalName", "qyouradio"
|
||||
VALUE "LegalCopyright", "Copyright (C) Ghostfox 2025"
|
||||
//VALUE "OriginalFilename", "appqyouradio.exe"
|
||||
VALUE "ProductName", "QYouRadio"
|
||||
VALUE "ProductVersion", "1.0.0.1"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x09, 1200
|
||||
END
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_ICON1 ICON "logo.ico"
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
Reference in New Issue
Block a user