5 Commits

Author SHA1 Message Date
Dark Steveneq
5ac6248c66 better and better 2025-10-20 02:53:30 +02:00
Dark Steveneq
c168086601 pretty minor case of suffering 2025-10-19 22:35:30 +02:00
629408b02e Merge pull request 'heugh jazz' (#1) from fuckedupgit into master
Reviewed-on: #1
2025-10-19 03:01:51 +02:00
Dark Steveneq
e66cf1b137 Reapply "dwjakld"
This reverts commit e091c97305.
2025-10-18 23:34:54 +02:00
Dark Steveneq
e091c97305 Revert "dwjakld"
This reverts commit 5c9b0ec49a.
2025-10-18 23:33:51 +02:00
9 changed files with 258 additions and 203 deletions

View File

@@ -21,6 +21,8 @@ qt_add_resources(appqyouvideo "resources"
resources/logo.png
)
set_source_files_properties(ComponentCache.qml
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
qt_add_qml_module(appqyouvideo
URI qyouvideo
@@ -31,6 +33,7 @@ qt_add_qml_module(appqyouvideo
ViewAbout.qml
ViewVideoList.qml
VideoPlayer.qml
ComponentCache.qml
)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
@@ -65,4 +68,3 @@ install(SCRIPT ${deploy_script})
add_subdirectory(QYRComponents)
project(qyouvideo VERSION 0.1 LANGUAGES CXX)

11
ComponentCache.qml Normal file
View File

@@ -0,0 +1,11 @@
pragma Singleton
import QtQuick 6.8
Item {
property var videoPlayer: Qt.createComponent("VideoPlayer.qml")
property var viewVideoList: Qt.createComponent("ViewVideoList.qml")
property var viewAbout: Qt.createComponent("ViewAbout.qml")
Component.onCompleted: Player.unloadVideo()
}

126
Main.qml
View File

@@ -11,87 +11,79 @@ ApplicationWindow {
height: 800
title: qsTr("QYouVideo")
ColumnLayout {
anchors.fill: parent
flags: Qt.Dialog
modality: Qt.ApplicationModal
Rectangle {
Layout.fillWidth: true
height: 43
header: Rectangle {
Layout.fillWidth: true
height: 43
color: Colors.surface0
color: Colors.surface0
RowLayout {
anchors.fill: parent
anchors.margins: 3
RowLayout {
anchors.fill: parent
anchors.margins: 3
Label {
Layout.leftMargin: 5
text: "QYouVideo"
heading: "h1"
}
Button {
// visible: stack.depth > 1
text: "Back"
onClicked: stack.popCurrentItem()
}
Item {
Layout.fillWidth: true
}
Label {
id: logo
// visible: stack.currentItem.StackView.index == 0
Layout.leftMargin: 5
text: "QYouVideo"
heading: "h1"
clip: true
}
TabBar {
id: tabbar
spacing: 10
background: Item{}
Item {
Layout.fillWidth: true
}
TabButton {
text: qsTr("Videos")
outlined: true
implicitWidth: 80
}
TabButton {
text: qsTr("About")
outlined: true
implicitWidth: 80
}
TabButton {
text: qsTr("Upload")
}
}
Button {
text: qsTr("Videos")
outlined: true
implicitWidth: 80
onClicked: if (!(stack.currentItem instanceof ViewVideoList)) stack.push(ComponentCache.viewVideoList.createObject(stack))
}
Button {
text: qsTr("About")
outlined: true
implicitWidth: 80
onClicked: if (!(stack.currentItem instanceof ViewAbout)) stack.push(ComponentCache.viewAbout.createObject(stack))
}
Button {
text: qsTr("Upload")
}
}
}
SwipeView {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 20
StackView {
id: stack
// __wheelAreaScrollSpeed: 50
interactive: false
currentIndex: tabbar.currentIndex
anchors.fill: parent
anchors.margins: 20
Loader {
// active: tabbar.currentIndex == 0
active: true
asynchronous: true
visible: status == Loader.Ready
source: "ViewVideoList.qml"
}
Loader {
// active: tabbar.currentIndex == 1
active: true
asynchronous: true
visible: status == Loader.Ready
source: "ViewAbout.qml"
}
Loader {
// active: tabbar.currentIndex == 2
active: true
asynchronous: true
visible: status == Loader.Ready
sourceComponent: Label {
heading: "h1"
text: "No idea if I'll ever implement this so don't worry >w<"
font.bold: true
}
}
initialItem: ViewVideoList {}
Component.onCompleted: contentItem.highlightMoveDuration = 160;
function openVideo(id) {
// if (stack.find((item, index) => {
// // If found player instance
// if (item.id == id) {
// // stack.pop(index, StackView.Immediate);
// stack.push(item)
// return true;
// }
// return false;
// }) == null) {
// If didn't find player instance
stack.push(ComponentCache.videoPlayer.createObject(stack, {id: id}));
// }
}
}
}

View File

@@ -3,6 +3,7 @@ import QtQuick.Controls 6.8
import QtQuick.Controls.Basic 6.8
BusyIndicator {
id: spinner
contentItem: Rectangle {
implicitWidth: 44
implicitHeight: 44
@@ -20,13 +21,13 @@ BusyIndicator {
radius: 12
}
// RotationAnimator {
// target: parent
// running: parent.parent.visible && parent.parent.running
// from: 0
// to: 360
// loops: Animation.Infinite
// duration: 1250
// }
RotationAnimator {
target: spinner
running: spinner.visible && spinner.running
from: 0
to: 360
loops: Animation.Infinite
duration: 1250
}
}
}

View File

@@ -75,6 +75,6 @@ Rectangle {
anchors.fill: parent
id: area
hoverEnabled: true
onClicked: Player.loadVideo(parent.id)
onClicked: stack.openVideo(parent.id)
}
}

View File

@@ -5,74 +5,82 @@ import QtQuick.Layouts 6.8
import QYRComponents 1.0
Rectangle {
visible: Player.active
color: Colors.surface1
radius: 10
Item {
// color: Colors.surface1
// radius: 10
property bool unrolled: true
StackView.onRemoved: destroy()
// width: childrenRect.width
height: unrolled ? 560 : 96
required property string id
property bool hasThumbnail: false
property bool loading: true
property bool failed: false
property string title
property string description
property string extension
property real position: 0
property real duration: 0
property real ratio
MouseArea {
anchors.fill: parent
height: 20
hoverEnabled: true
propagateComposedEvents: true
anchors.fill: parent
onClicked: unrolled = true;
onContainsMouseChanged: function() {
if (!containsMouse) {
unrolled = false;
}
Component.onCompleted: Player.onPositionChanged = () => {
console.log("test print");
if (Player.id == id) {
position = Player.position
}
}
Component.onDestruction: if (Player.id == id) Player.unloadVideo()
onPositionChanged: if (Player.id == id) Player.position = position
ColumnLayout {
visible: !loading && !failed
anchors.fill: parent
Item {
visible: unrolled
Layout.fillHeight: true
Image {
Layout.fillWidth: true
// height: width * parent.parent.ratio
asynchronous: true
cache: true
source: parent.parent.hasThumbnail ? ("https://youvideo.nonamesoft.xyz/thumbnails/" + parent.parent.id) : "https://youvideo.nonamesoft.xyz/thumbnails/audio.png"
fillMode: Image.PreserveAspectFit
}
RowLayout {
Button {
text: Player.loading ? "Loading" : (Player.playing ? "Pause" : "Play")
enabled: !Player.loading
text: Player.id == id ? (Player.loading ? "Loading" : (Player.playing ? "Pause" : "Play")) : "Play"
enabled: Player.id != id || !(Player.loading && Player.id == id)
onClicked: function() {
if (Player.id != id) {
Player.loadVideo(parent.parent.parent.id, parent.parent.parent.extension, parent.parent.parent.title, parent.parent.parent.position);
}
if (Player.playing) {
Player.pause()
Player.pause();
} else {
Player.play();
}
}
}
Button {
text: "Stop"
onClicked: Player.stop()
}
Label {
heading: "h4"
text: new Date(Player.position).toISOString().slice(14, 19)
text: new Date(parent.parent.parent.position).toISOString().slice(14, 19)
}
Slider {
Layout.fillWidth: true
from: 0
to: Player.duration
value: Player.position
buffered: Player.buffered
onMoved: Player.position = value
to: parent.parent.parent.duration
value: parent.parent.parent.position
buffered: Player.id == id ? Player.buffered : 0
onMoved: parent.parent.parent.position = value
}
Label {
heading: "h4"
text: new Date(Player.duration).toISOString().slice(14, 19)
text: new Date(parent.parent.parent.duration).toISOString().slice(14, 19)
}
Label {
@@ -91,19 +99,102 @@ Rectangle {
Layout.fillWidth: true
wrapMode: Text.WordWrap
clip: true
text: Player.title
text: parent.parent.title
heading: "h2"
font.bold: true
}
Label {
visible: unrolled
Layout.fillWidth: true
wrapMode: Text.WordWrap
clip: true
text: Player.description
text: parent.parent.description
heading: "h4"
font.bold: true
}
Item {
Layout.fillHeight: true
}
}
BusyIndicator {
anchors.centerIn: parent
visible: loading
running: loading
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
anchors.centerIn: parent
visible: failed
Label {
text: "Couldn't get video details!"
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.parent.fetchData()
}
}
Timer {
interval: 0
running: true
onTriggered: parent.fetchData()
}
function fetchData() {
loading = true;
failed = false;
const xhr = new XMLHttpRequest;
xhr.open("GET", "https://youvideo.nonamesoft.xyz/youvideo/video/" + id);
xhr.onreadystatechange = function() {
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 video metadata");
title = data.name;
description = data.description;
extension = data.extension;
duration = data.metadata.duration * 1000;
ratio = data.metadata.size[0] / data.metadata.size[1];
if (ratio == NaN) {
ratio = 1.77777;
}
console.log(ratio);
if (Player.id == id) {
console.log("Already playing, this is not intended but possible in its current state so it's a feature, not a bug :3");
return;
}
Player.loadVideo(id, extension, title, position)
}
}
xhr.ontimeout = function() {
loading = false;
failed = true;
console.log("Request timed out");
}
xhr.send();
}
}

View File

@@ -7,16 +7,14 @@ import QYRComponents 1.0
Item {
anchors.fill: parent
property bool loading: true
property bool failed: false
GridView {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Player.active ? 96 : 0
width: Math.floor(parent.width / cellWidth) * cellWidth
visible: !loading || !failed
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
model: ListModel { id: model }
clip: true
@@ -24,6 +22,8 @@ Item {
cellHeight: 224
delegate: VideoEntry {}
Component.onCompleted: console.log(parent.width / cellWidth)
}
BusyIndicator {
@@ -56,12 +56,12 @@ Item {
Button {
text: "Retry"
onClicked: parent.fetchData()
onClicked: parent.parent.fetchData()
}
}
Timer {
interval: 1000
interval: 0
running: true
onTriggered: parent.fetchData()
}
@@ -71,7 +71,7 @@ Item {
loading = true;
failed = false;
const xhr = new XMLHttpRequest;
xhr.open("GET", "http://youvideo.nonamesoft.xyz/youvideo/api/videos");
xhr.open("GET", "https://youvideo.nonamesoft.xyz/youvideo/api/videos");
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
loading = false;
@@ -94,11 +94,4 @@ Item {
}
xhr.send();
}
VideoPlayer {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
Layout.fillWidth: true
}
}

View File

@@ -1,17 +1,17 @@
#include "player.h"
// HOLY GRAIL
// https://doc.qt.io/qt-6/qaudiobufferoutput.htmli t
// https://doc.qt.io/qt-6/qaudiobufferoutput.html
Player::Player(QObject *parent) : QObject(parent),
player(this), output(this), manager(this)
player(this), output(this)
{
QObject::connect(&this->player, &QMediaPlayer::mediaStatusChanged, this, [this] () {
const QMediaPlayer::MediaStatus status = this->player.mediaStatus();
if (status == QMediaPlayer::NoMedia)
{
qDebug("Player::Player(): NoMedia");
this->stop();
this->unloadVideo();
}
else if (status == QMediaPlayer::BufferingMedia || status == QMediaPlayer::LoadingMedia)
{
@@ -26,13 +26,12 @@ Player::Player(QObject *parent) : QObject(parent),
}
else
{
if (this->m_position == 0)
{
this->player.play();
}
this->m_loading = false;
emit this->loadingChanged();
if (!this->m_initialLoadFinished)
{
this->m_initialLoadFinished = true;
this->play();
}
}
});
@@ -41,10 +40,10 @@ Player::Player(QObject *parent) : QObject(parent),
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::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;
@@ -107,47 +106,28 @@ void Player::setPosition(float newPosition)
// Methods
// ***********
void Player::loadVideo(QString id)
// For QYouRadio:
// QEventLoop loop(this);
// QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
// loop.exec();
// const QJsonDocument data = QJsonDocument::fromJson(reply->readAll());
void Player::loadVideo(QString id, QString extension, QString title, float position)
{
if (this->m_active)
if (this->m_id != id)
{
this->unloadVideo();
}
QNetworkReply* reply = this->manager.get(QNetworkRequest(QUrl("https://youvideo.nonamesoft.xyz/youvideo/video/" + id)));
QEventLoop loop(this);
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << "Player::loadVideo(): Video " << id << " doesn't exist!\n";
delete reply;
return;
}
const QJsonDocument data = QJsonDocument::fromJson(reply->readAll());
this->player.setSource(QUrl("https://youvideo.nonamesoft.xyz/youvideo/api/videofile/with_extension/" + id + data ["extension"].toString()));
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->player.setSource(QUrl("https://youvideo.nonamesoft.xyz/youvideo/api/videofile/with_extension/" + id + extension));
this->m_active = true;
emit this->activeChanged();
this->m_title = title;
emit this->titleChanged();
this->m_duration = data["metadata"]["duration"].toDouble() * 1000;
emit this->durationChanged();
this->m_position = 0;
this->m_position = position;
emit this->positionChanged();
this->m_buffered = 0;
@@ -159,24 +139,31 @@ void Player::loadVideo(QString id)
this->m_failed = false;
emit this->failedChanged();
qDebug() << "Player::loadVideo(): Loaded video https://youvideo.nonamesoft.xyz/youvideo/api/videofile/with_extension/" + id + data ["extension"].toString();
delete reply;
qDebug() << "Player::loadVideo(): Loaded video https://youvideo.nonamesoft.xyz/youvideo/api/videofile/with_extension/" + id + extension;
}
void Player::unloadVideo() {
qDebug("Player::unloadVideo(): Video unloaded");
this->m_id = "";
emit this->idChanged();
this->player.setSource(QUrl(""));
this->player.stop();
this->m_title = "";
emit this->titleChanged();
this->m_loading = false;
emit this->loadingChanged();
this->m_failed = false;
emit this->activeChanged();
this->m_active = false;
emit this->activeChanged();
emit this->failedChanged();
}
void Player::play()
{
if (!this->m_active || this->m_loading)
if (this->m_id.length() == 0 || this->m_loading)
{
return;
}
@@ -185,19 +172,10 @@ void Player::play()
void Player::pause()
{
if (!this->m_active || this->m_loading)
if (this->m_id.length() == 0 || this->m_loading)
{
return;
}
this->player.pause();
}
void Player::stop()
{
if (!this->m_active || this->m_loading)
{
return;
}
this->player.stop();
this->unloadVideo();
}

View File

@@ -4,7 +4,6 @@
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QObject>
#include <QNetworkAccessManager>
#include <QtQmlIntegration/qqmlintegration.h>
class Player : public QObject
@@ -14,10 +13,7 @@ class Player : public QObject
QML_ELEMENT
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)
@@ -36,18 +32,14 @@ public:
void setVolume(float newVolume);
public slots:
void loadVideo(QString id);
void loadVideo(QString id, QString extension, QString title, float position);
void unloadVideo();
void play();
void pause();
void stop();
signals:
void titleChanged();
void descriptionChanged();
void idChanged();
void activeChanged();
void durationChanged();
void positionChanged();
void bufferedChanged();
void loadingChanged();
@@ -58,16 +50,11 @@ signals:
private:
QMediaPlayer player;
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;
};