mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-06-17 06:01:12 +00:00
Add automatic update checking. Fixes #8
This commit is contained in:
+5
-2
@@ -82,7 +82,8 @@ SOURCES += \
|
|||||||
streaming/audio.cpp \
|
streaming/audio.cpp \
|
||||||
gui/computermodel.cpp \
|
gui/computermodel.cpp \
|
||||||
gui/appmodel.cpp \
|
gui/appmodel.cpp \
|
||||||
streaming/streamutils.cpp
|
streaming/streamutils.cpp \
|
||||||
|
backend/autoupdatechecker.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
utils.h \
|
utils.h \
|
||||||
@@ -97,7 +98,8 @@ HEADERS += \
|
|||||||
gui/computermodel.h \
|
gui/computermodel.h \
|
||||||
gui/appmodel.h \
|
gui/appmodel.h \
|
||||||
streaming/video/decoder.h \
|
streaming/video/decoder.h \
|
||||||
streaming/streamutils.h
|
streaming/streamutils.h \
|
||||||
|
backend/autoupdatechecker.h
|
||||||
|
|
||||||
# Platform-specific renderers and decoders
|
# Platform-specific renderers and decoders
|
||||||
ffmpeg {
|
ffmpeg {
|
||||||
@@ -203,3 +205,4 @@ macx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VERSION = 0.1.0
|
VERSION = 0.1.0
|
||||||
|
DEFINES += VERSION_STR=\\\"0.1.0\\\"
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
#include "autoupdatechecker.h"
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
AutoUpdateChecker::AutoUpdateChecker(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_Nam(this)
|
||||||
|
{
|
||||||
|
// Never communicate over HTTP
|
||||||
|
m_Nam.setStrictTransportSecurityEnabled(true);
|
||||||
|
|
||||||
|
connect(&m_Nam, SIGNAL(finished(QNetworkReply*)),
|
||||||
|
this, SLOT(handleUpdateCheckRequestFinished(QNetworkReply*)));
|
||||||
|
|
||||||
|
QString currentVersion(VERSION_STR);
|
||||||
|
qDebug() << "Current Moonlight version:" << currentVersion;
|
||||||
|
parseStringToVersionQuad(currentVersion, m_CurrentVersionQuad);
|
||||||
|
|
||||||
|
// Should at least have a 1.0-style version number
|
||||||
|
Q_ASSERT(m_CurrentVersionQuad.count() > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoUpdateChecker::start()
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_WIN32) || defined(Q_OS_DARWIN) // Only run update checker on platforms without auto-update
|
||||||
|
// We'll get a callback when this is finished
|
||||||
|
QUrl url("https://moonlight-stream.com/updates/qt.json");
|
||||||
|
m_Nam.get(QNetworkRequest(url));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoUpdateChecker::parseStringToVersionQuad(QString& string, QVector<int>& version)
|
||||||
|
{
|
||||||
|
QStringList list = string.split('.');
|
||||||
|
for (const QString& component : list) {
|
||||||
|
version.append(component.toInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoUpdateChecker::handleUpdateCheckRequestFinished(QNetworkReply* reply)
|
||||||
|
{
|
||||||
|
Q_ASSERT(reply->isFinished());
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
QTextStream stream(reply);
|
||||||
|
stream.setCodec("UTF-8");
|
||||||
|
|
||||||
|
// Read all data and queue the reply for deletion
|
||||||
|
QString jsonString = stream.readAll();
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8(), &error);
|
||||||
|
if (jsonDoc.isNull()) {
|
||||||
|
qWarning() << "Update manifest malformed:" << error.errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray array = jsonDoc.array();
|
||||||
|
if (array.isEmpty()) {
|
||||||
|
qWarning() << "Update manifest doesn't contain an array";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QJsonValueRef updateEntry : array) {
|
||||||
|
if (updateEntry.isObject()) {
|
||||||
|
QJsonObject updateObj = updateEntry.toObject();
|
||||||
|
if (!updateObj.contains("platform") ||
|
||||||
|
!updateObj.contains("arch") ||
|
||||||
|
!updateObj.contains("version") ||
|
||||||
|
!updateObj.contains("browser_url")) {
|
||||||
|
qWarning() << "Update manifest entry missing vital field";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!updateObj["platform"].isString() ||
|
||||||
|
!updateObj["arch"].isString() ||
|
||||||
|
!updateObj["version"].isString() ||
|
||||||
|
!updateObj["browser_url"].isString()) {
|
||||||
|
qWarning() << "Update manifest entry has unexpected vital field type";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateObj["arch"] == QSysInfo::buildCpuArchitecture() &&
|
||||||
|
updateObj["platform"] == QSysInfo::productType()) {
|
||||||
|
qDebug() << "Found update manifest match for current platform";
|
||||||
|
|
||||||
|
QString latestVersion = updateObj["version"].toString();
|
||||||
|
qDebug() << "Latest version of Moonlight for this platform is:" << latestVersion;
|
||||||
|
|
||||||
|
QVector<int> latestVersionQuad;
|
||||||
|
|
||||||
|
parseStringToVersionQuad(latestVersion, latestVersionQuad);
|
||||||
|
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
int latestVer = 0;
|
||||||
|
int currentVer = 0;
|
||||||
|
|
||||||
|
// Treat missing decimal places as 0
|
||||||
|
if (i < latestVersionQuad.count()) {
|
||||||
|
latestVer = latestVersionQuad[i];
|
||||||
|
}
|
||||||
|
if (i < m_CurrentVersionQuad.count()) {
|
||||||
|
currentVer = m_CurrentVersionQuad[i];
|
||||||
|
}
|
||||||
|
if (i >= latestVersionQuad.count() && i >= m_CurrentVersionQuad.count()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVer < latestVer) {
|
||||||
|
qDebug() << "Update available";
|
||||||
|
|
||||||
|
emit onUpdateAvailable(updateObj["browser_url"].toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (currentVer > latestVer) {
|
||||||
|
qDebug() << "Update manifest version lower than current version";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Update manifest version equal to current version";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << "Update manifest contained unrecognized entry:" << updateEntry.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << "No entry in update manifest found for current platform: "
|
||||||
|
<< QSysInfo::buildCpuArchitecture() << QSysInfo::productType();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << "Update checking failed with error: " << reply->error();
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
|
class AutoUpdateChecker : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit AutoUpdateChecker(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
Q_INVOKABLE void start();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void onUpdateAvailable(QString url);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleUpdateCheckRequestFinished(QNetworkReply* reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void parseStringToVersionQuad(QString& string, QVector<int>& version);
|
||||||
|
|
||||||
|
QVector<int> m_CurrentVersionQuad;
|
||||||
|
QNetworkAccessManager m_Nam;
|
||||||
|
};
|
||||||
+32
-3
@@ -4,6 +4,8 @@ import QtQuick.Layouts 1.3
|
|||||||
|
|
||||||
import QtQuick.Controls.Material 2.1
|
import QtQuick.Controls.Material 2.1
|
||||||
|
|
||||||
|
import AutoUpdateChecker 1.0
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: window
|
id: window
|
||||||
visible: true
|
visible: true
|
||||||
@@ -66,6 +68,36 @@ ApplicationWindow {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
property string browserUrl: ""
|
||||||
|
|
||||||
|
id: updateButton
|
||||||
|
icon.source: "qrc:/res/update.svg"
|
||||||
|
|
||||||
|
// Invisible until we get a callback notifying us that
|
||||||
|
// an update is available
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
onClicked: Qt.openUrlExternally(browserUrl);
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
x: parent.width
|
||||||
|
transformOrigin: Menu.TopRight
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateAvailable(url)
|
||||||
|
{
|
||||||
|
updateButton.browserUrl = url
|
||||||
|
updateButton.visible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
AutoUpdateChecker.onUpdateAvailable.connect(updateAvailable)
|
||||||
|
AutoUpdateChecker.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ToolButton {
|
ToolButton {
|
||||||
icon.source: "qrc:/res/question_mark.svg"
|
icon.source: "qrc:/res/question_mark.svg"
|
||||||
|
|
||||||
@@ -73,7 +105,6 @@ ApplicationWindow {
|
|||||||
onClicked: Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
|
onClicked: Qt.openUrlExternally("https://github.com/moonlight-stream/moonlight-docs/wiki/Setup-Guide");
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
id: helpButton
|
|
||||||
x: parent.width
|
x: parent.width
|
||||||
transformOrigin: Menu.TopRight
|
transformOrigin: Menu.TopRight
|
||||||
}
|
}
|
||||||
@@ -87,7 +118,6 @@ ApplicationWindow {
|
|||||||
onClicked: navigateTo("qrc:/gui/GamepadMapper.qml", "Gamepad Mapping")
|
onClicked: navigateTo("qrc:/gui/GamepadMapper.qml", "Gamepad Mapping")
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
id: gamepadMappingMenu
|
|
||||||
x: parent.width
|
x: parent.width
|
||||||
transformOrigin: Menu.TopRight
|
transformOrigin: Menu.TopRight
|
||||||
}
|
}
|
||||||
@@ -98,7 +128,6 @@ ApplicationWindow {
|
|||||||
onClicked: navigateTo("qrc:/gui/SettingsView.qml", "Settings")
|
onClicked: navigateTo("qrc:/gui/SettingsView.qml", "Settings")
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
id: optionsMenu
|
|
||||||
x: parent.width
|
x: parent.width
|
||||||
transformOrigin: Menu.TopRight
|
transformOrigin: Menu.TopRight
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "gui/computermodel.h"
|
#include "gui/computermodel.h"
|
||||||
#include "gui/appmodel.h"
|
#include "gui/appmodel.h"
|
||||||
|
#include "backend/autoupdatechecker.h"
|
||||||
#include "streaming/session.hpp"
|
#include "streaming/session.hpp"
|
||||||
#include "settings/streamingpreferences.h"
|
#include "settings/streamingpreferences.h"
|
||||||
|
|
||||||
@@ -172,6 +173,11 @@ int main(int argc, char *argv[])
|
|||||||
[](QQmlEngine*, QJSEngine*) -> QObject* {
|
[](QQmlEngine*, QJSEngine*) -> QObject* {
|
||||||
return new ComputerManager();
|
return new ComputerManager();
|
||||||
});
|
});
|
||||||
|
qmlRegisterSingletonType<AutoUpdateChecker>("AutoUpdateChecker", 1, 0,
|
||||||
|
"AutoUpdateChecker",
|
||||||
|
[](QQmlEngine*, QJSEngine*) -> QObject* {
|
||||||
|
return new AutoUpdateChecker();
|
||||||
|
});
|
||||||
|
|
||||||
QQuickStyle::setStyle("Material");
|
QQuickStyle::setStyle("Material");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#009624" version="1.1" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path d="M21,10.12H14.22L16.96,7.3C14.23,4.6 9.81,4.5 7.08,7.2C4.35,9.91 4.35,14.28 7.08,17C9.81,19.7 14.23,19.7 16.96,17C18.32,15.65 19,14.08 19,12.1H21C21,14.08 20.12,16.65 18.36,18.39C14.85,21.87 9.15,21.87 5.64,18.39C2.14,14.92 2.11,9.28 5.62,5.81C9.13,2.34 14.76,2.34 18.27,5.81L21,3V10.12M12.5,8V12.25L16,14.33L15.28,15.54L11,13V8H12.5Z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 651 B |
@@ -13,5 +13,6 @@
|
|||||||
<file>res/arrow_left.svg</file>
|
<file>res/arrow_left.svg</file>
|
||||||
<file>res/question_mark.svg</file>
|
<file>res/question_mark.svg</file>
|
||||||
<file>res/moonlight.svg</file>
|
<file>res/moonlight.svg</file>
|
||||||
|
<file>res/update.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
Reference in New Issue
Block a user