heugh jazz #1

Merged
ghostfox merged 1 commits from fuckedupgit into master 2025-10-19 03:01:51 +02:00
9 changed files with 358 additions and 55 deletions

1
.gitignore vendored
View File

@@ -32,6 +32,7 @@ qrc_*.cpp
Thumbs.db
*.res
*.rc
!resources/qyouvideo.rc
/.qmake.cache
/.qmake.stash

View File

@@ -0,0 +1,32 @@
import QtQuick 6.8
import QtQuick.Controls 6.8
import QtQuick.Controls.Basic 6.8
BusyIndicator {
contentItem: Rectangle {
implicitWidth: 44
implicitHeight: 44
color: "transparent"
radius: 44
border.width: 4
border.color: Colors.primaryAlt
Rectangle {
width: 12
height: 12
anchors.top: parent.top
anchors.topMargin: 8
color: Colors.primary
radius: 12
}
// RotationAnimator {
// target: parent
// running: parent.parent.visible && parent.parent.running
// from: 0
// to: 360
// loops: Animation.Infinite
// duration: 1250
// }
}
}

View File

@@ -25,6 +25,7 @@ qt_add_qml_module(QYRComponents
Label.qml
TabButton.qml
YouAds.qml
QML_FILES BusyIndicator.qml
# SOURCES qyrcomponents.cpp qyrcomponents.h
)

View File

@@ -5,6 +5,8 @@ import QtQuick.Controls.Basic 6.8
Slider {
snapMode: Slider.SnapAlways
property real buffered
implicitWidth: 130
implicitHeight: 20
@@ -26,6 +28,17 @@ Slider {
background: Rectangle {
color: "#555"
radius: 5
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
visible: parent.parent.buffered != 0
width: parent.width * parent.parent.buffered
color: "#888"
radius: 5
}
}
handle: Rectangle {

View File

@@ -6,18 +6,41 @@ import QtQuick.Layouts 6.8
import QYRComponents 1.0
Rectangle {
visible: Player.hasVideo
visible: Player.active
color: Colors.surface1
radius: 10
property bool unrolled: true
// width: childrenRect.width
height: 64
height: unrolled ? 560 : 96
MouseArea {
anchors.fill: parent
height: 20
hoverEnabled: true
propagateComposedEvents: true
onClicked: unrolled = true;
onContainsMouseChanged: function() {
if (!containsMouse) {
unrolled = false;
}
}
}
ColumnLayout {
anchors.fill: parent
Item {
visible: unrolled
Layout.fillHeight: true
}
RowLayout {
Button {
text: Player.playing ? "Pause" : "Play"
text: Player.loading ? "Loading" : (Player.playing ? "Pause" : "Play")
enabled: !Player.loading
onClicked: function() {
if (Player.playing) {
Player.pause()
@@ -32,10 +55,28 @@ Rectangle {
onClicked: Player.stop()
}
Label {
heading: "h4"
text: new Date(Player.position).toISOString().slice(14, 19)
}
Slider {
Layout.fillWidth: true
from: 0
to: 106
to: Player.duration
value: Player.position
buffered: Player.buffered
onMoved: Player.position = value
}
Label {
heading: "h4"
text: new Date(Player.duration).toISOString().slice(14, 19)
}
Label {
text: Player.buffered
}
Slider {
@@ -45,5 +86,24 @@ Rectangle {
onMoved: Player.volume = value
}
}
Label {
Layout.fillWidth: true
wrapMode: Text.WordWrap
clip: true
text: Player.title
heading: "h2"
font.bold: true
}
Label {
visible: unrolled
Layout.fillWidth: true
wrapMode: Text.WordWrap
clip: true
text: Player.description
heading: "h4"
font.bold: true
}
}
}

View File

@@ -4,23 +4,19 @@ import QtQuick.Layouts 6.8
import QYRComponents 1.0
ColumnLayout {
Item {
anchors.fill: parent
property bool loading: true
property bool failed: false
VideoPlayer {
Layout.fillWidth: true
Layout.fillHeight: false
}
GridView {
Layout.fillWidth: true
Layout.fillHeight: true
// anchors.horizontalCenter: parent.horizontalCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Player.active ? 96 : 0
visible: !loading || !failed
// anchors.fill: parent
anchors.fill: parent
model: ListModel { id: model }
clip: true
@@ -28,8 +24,6 @@ ColumnLayout {
cellHeight: 224
delegate: VideoEntry {}
Component.onCompleted: parent.fetchData()
}
BusyIndicator {
@@ -66,6 +60,12 @@ ColumnLayout {
}
}
Timer {
interval: 1000
running: true
onTriggered: parent.fetchData()
}
function fetchData() {
model.clear();
loading = true;
@@ -76,15 +76,29 @@ ColumnLayout {
if (xhr.readyState == XMLHttpRequest.DONE) {
loading = false;
if (xhr.status != 200) {
console.log("Invalid response");
failed = true;
return;
}
const data = JSON.parse(xhr.responseText);
console.log("Received data, found " + data.length + " videos");
data.forEach(video => {
model.append(video);
});
}
}
xhr.ontimeout = function() {
loading = false;
failed = true;
console.log("Request timed out");
}
xhr.send();
}
VideoPlayer {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
Layout.fillWidth: true
}
}

View File

@@ -1,15 +1,59 @@
#include "player.h"
// Public
// HOLY GRAIL
// https://doc.qt.io/qt-6/qaudiobufferoutput.htmli t
Player::Player(QObject *parent) : QObject(parent),
player(this), output(this), manager(this)
{
QObject::connect(&this->player, &QMediaPlayer::mediaStatusChanged, this, [this] () {
emit this->hasVideoChanged();
emit this->loadingChanged();
const QMediaPlayer::MediaStatus status = this->player.mediaStatus();
if (status == QMediaPlayer::NoMedia)
{
qDebug("Player::Player(): NoMedia");
this->stop();
}
else if (status == QMediaPlayer::BufferingMedia || status == QMediaPlayer::LoadingMedia)
{
qDebug("Player::Player() loadingChanged");
this->m_loading = true;
emit this->loadingChanged();
}
else if (status == QMediaPlayer::EndOfMedia)
{
qDebug("Player::Player() playingChanged");
emit this->playingChanged();
}
else
{
this->m_loading = false;
emit this->loadingChanged();
if (!this->m_initialLoadFinished)
{
this->m_initialLoadFinished = true;
this->play();
}
}
});
QObject::connect(&this->player, &QMediaPlayer::positionChanged, this, [this] () {
this->m_position = this->player.position();
emit this->positionChanged();
});
QObject::connect(&this->player, &QMediaPlayer::bufferProgressChanged, this, [this] () {
this->m_buffered = this->player.bufferedTimeRange().latestTime() / this->m_duration;
emit this->bufferedChanged();
});
QObject::connect(&this->player, &QMediaPlayer::errorOccurred, this, [this] () {
this->m_failed = true;
qDebug("Player::Player() failedChanged");
emit this->failedChanged();
});
QObject::connect(&this->player, &QMediaPlayer::playbackStateChanged, this, [this] () {
qDebug("Player::Player() playingChanged");
emit this->playingChanged();
});
@@ -28,20 +72,10 @@ 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;
}
// ********
// Read
// ********
bool Player::playing() const
{
@@ -53,15 +87,29 @@ float Player::volume() const
return this->output.volume();
}
// *********
// Write
// *********
void Player::setVolume(float newVolume)
{
this->output.setVolume(newVolume);
}
// Public slots
void Player::setPosition(float newPosition)
{
this->player.setPosition(newPosition);
}
// ***********
// Methods
// ***********
void Player::loadVideo(QString id)
{
if (this->hasVideo())
if (this->m_active)
{
this->unloadVideo();
}
@@ -82,8 +130,32 @@ void Player::loadVideo(QString id)
this->player.setSource(QUrl("https://youvideo.nonamesoft.xyz/youvideo/api/videofile/with_extension/" + id + data ["extension"].toString()));
emit this->hasVideoChanged();
this->m_title = data["name"].toString();
emit this->titleChanged();
this->m_description = data["description"].toString();
emit this->descriptionChanged();
this->m_id = id;
emit this->idChanged();
this->m_initialLoadFinished = false;
this->m_active = true;
emit this->activeChanged();
this->m_duration = data["metadata"]["duration"].toDouble() * 1000;
emit this->durationChanged();
this->m_position = 0;
emit this->positionChanged();
this->m_buffered = 0;
emit this->bufferedChanged();
this->m_loading = true;
emit this->loadingChanged();
this->m_failed = false;
emit this->failedChanged();
@@ -97,17 +169,14 @@ 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();
emit this->activeChanged();
this->m_active = false;
emit this->activeChanged();
}
void Player::play()
{
qDebug() << "Player::play(): Can't play video?" << this->hasVideo() << this->loading();
if (!this->hasVideo() || this->loading())
if (!this->m_active || this->m_loading)
{
return;
}
@@ -116,8 +185,7 @@ void Player::play()
void Player::pause()
{
qDebug() << "Player::pause(): Can't pause video?" << (!this->hasVideo() || this->loading());
if (!this->hasVideo() || this->loading())
if (!this->m_active || this->m_loading)
{
return;
}
@@ -126,8 +194,7 @@ void Player::pause()
void Player::stop()
{
qDebug() << "Player::stop(): Can't stop video?" << (!this->hasVideo() || this->loading());
if (!this->hasVideo() || this->loading())
if (!this->m_active || this->m_loading)
{
return;
}

View File

@@ -13,22 +13,26 @@ class Player : public QObject
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(QString title MEMBER m_title NOTIFY titleChanged FINAL)
Q_PROPERTY(QString description MEMBER m_description NOTIFY descriptionChanged FINAL)
Q_PROPERTY(QString id MEMBER m_id NOTIFY idChanged FINAL)
Q_PROPERTY(bool active MEMBER m_active NOTIFY activeChanged FINAL)
Q_PROPERTY(float duration MEMBER m_duration NOTIFY durationChanged FINAL)
Q_PROPERTY(float position MEMBER m_position WRITE setPosition NOTIFY positionChanged FINAL)
Q_PROPERTY(float buffered MEMBER m_buffered NOTIFY bufferedChanged FINAL)
Q_PROPERTY(bool loading MEMBER m_loading NOTIFY loadingChanged FINAL)
Q_PROPERTY(bool failed MEMBER m_failed NOTIFY failedChanged FINAL)
Q_PROPERTY(bool playing READ playing NOTIFY playingChanged FINAL)
Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged FINAL)
public:
Player(QObject *parent = nullptr);
~Player();
bool hasVideo() const;
bool loading() const;
bool failed() const;
bool playing() const;
float volume() const;
void setPosition(float newPosition);
void setVolume(float newVolume);
public slots:
@@ -39,7 +43,13 @@ public slots:
void stop();
signals:
void hasVideoChanged();
void titleChanged();
void descriptionChanged();
void idChanged();
void activeChanged();
void durationChanged();
void positionChanged();
void bufferedChanged();
void loadingChanged();
void failedChanged();
void playingChanged();
@@ -50,5 +60,14 @@ private:
QAudioOutput output;
QNetworkAccessManager manager;
QString m_title = "";
QString m_description = "";
QString m_id = "";
float m_duration = 0;
float m_position = 0;
float m_buffered = 0;
bool m_initialLoadFinished = false;
bool m_active = false;
bool m_loading = false;
bool m_failed = false;
};

96
resources/qyouvideo.rc Normal file
View File

@@ -0,0 +1,96 @@
// 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", "QYouVideo"
VALUE "FileVersion", "1.0.0.1"
VALUE "InternalName", "qyouvideo"
VALUE "LegalCopyright", "Copyright (C) Ghostfox 2025"
//VALUE "OriginalFilename", "appqyouvideo.exe"
VALUE "ProductName", "QYouVideo"
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