diff --git a/CMakeLists.txt b/CMakeLists.txt index afa9db9..cc96446 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/ComponentCache.qml b/ComponentCache.qml new file mode 100644 index 0000000..e07ed99 --- /dev/null +++ b/ComponentCache.qml @@ -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() +} diff --git a/Main.qml b/Main.qml index 5c338b9..08a6b46 100755 --- a/Main.qml +++ b/Main.qml @@ -24,27 +24,21 @@ ApplicationWindow { anchors.fill: parent anchors.margins: 3 - Label { - Layout.leftMargin: 5 - text: "QYouVideo" - heading: "h1" - } - Button { - visible: stack.currentItem.StackView.index > 0 + // visible: stack.depth > 1 text: "Back" onClicked: stack.popCurrentItem() } Label { - heading: "h2" - text: stack.currentItem.StackView.index + id: logo + // visible: stack.currentItem.StackView.index == 0 + Layout.leftMargin: 5 + text: "QYouVideo" + heading: "h1" + clip: true } - Label { - heading: "h2" - text: stack.depth - } Item { Layout.fillWidth: true @@ -54,12 +48,13 @@ ApplicationWindow { text: qsTr("Videos") outlined: true implicitWidth: 80 - onClicked: if (stack.currentItem.StackView.index > 0) stack.push(stackVideoList) + 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") @@ -69,13 +64,12 @@ ApplicationWindow { StackView { id: stack + // __wheelAreaScrollSpeed: 50 anchors.fill: parent anchors.margins: 20 - initialItem: ViewVideoList { - id: stackVideoList - } + initialItem: ViewVideoList {} function openVideo(id) { // if (stack.find((item, index) => { @@ -88,9 +82,7 @@ ApplicationWindow { // return false; // }) == null) { // If didn't find player instance - const component = Qt.createComponent("VideoPlayer.qml"); - const item = component.createObject(stack, {id: id}) - stack.push(item); + stack.push(ComponentCache.videoPlayer.createObject(stack, {id: id})); // } } } diff --git a/QYRComponents/BusyIndicator.qml b/QYRComponents/BusyIndicator.qml index 8cdf62b..cb6f10c 100644 --- a/QYRComponents/BusyIndicator.qml +++ b/QYRComponents/BusyIndicator.qml @@ -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 + } } } diff --git a/VideoPlayer.qml b/VideoPlayer.qml index 074b97d..f6c8868 100644 --- a/VideoPlayer.qml +++ b/VideoPlayer.qml @@ -9,6 +9,8 @@ Item { // color: Colors.surface1 // radius: 10 + StackView.onRemoved: destroy() + required property string id property bool hasThumbnail: false property bool loading: true @@ -22,13 +24,22 @@ Item { anchors.fill: parent + 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 Image { Layout.fillWidth: true - height: width * parent.parent.ratio + // 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" @@ -37,25 +48,24 @@ Item { 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) } @@ -64,8 +74,8 @@ Item { from: 0 to: parent.parent.parent.duration value: parent.parent.parent.position - buffered: Player.buffered - onMoved: Player.position = value + buffered: Player.id == id ? Player.buffered : 0 + onMoved: parent.parent.parent.position = value } Label { @@ -138,12 +148,12 @@ Item { Button { text: "Retry" - onClicked: parent.fetchData() + onClicked: parent.parent.fetchData() } } Timer { - interval: 1000 + interval: 0 running: true onTriggered: parent.fetchData() } @@ -166,8 +176,18 @@ Item { 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() { diff --git a/ViewVideoList.qml b/ViewVideoList.qml index 49f7b7d..d7e0506 100644 --- a/ViewVideoList.qml +++ b/ViewVideoList.qml @@ -11,12 +11,10 @@ Item { property bool failed: false GridView { - width: Math.floor(cellWidth / parent.width) * cellWidth - 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 @@ -25,7 +23,7 @@ Item { delegate: VideoEntry {} - Component.onCompleted: console.log(cellWidth / parent.width) + Component.onCompleted: console.log(parent.width / cellWidth) } BusyIndicator { @@ -58,12 +56,12 @@ Item { Button { text: "Retry" - onClicked: parent.fetchData() + onClicked: parent.parent.fetchData() } } Timer { - interval: 1000 + interval: 0 running: true onTriggered: parent.fetchData() } diff --git a/player.cpp b/player.cpp index 61a381d..f4bd4d4 100644 --- a/player.cpp +++ b/player.cpp @@ -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(); -} diff --git a/player.h b/player.h index e19b061..de521e2 100644 --- a/player.h +++ b/player.h @@ -4,7 +4,6 @@ #include #include #include -#include #include 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; };