1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2025-09-04 01:42:44 +00:00

Add support for server-side notifications

This commit is contained in:
Henk Kalkwater 2021-09-09 02:18:10 +02:00
parent 357ac89330
commit 60bc90c5fa
No known key found for this signature in database
GPG key ID: A69C050E9FD9FF6A
14 changed files with 179 additions and 14 deletions

View file

@ -100,7 +100,7 @@ public:
Q_PROPERTY(QString userId READ userId NOTIFY userIdChanged)
Q_PROPERTY(QJsonObject deviceProfile READ deviceProfileJson NOTIFY deviceProfileChanged)
Q_PROPERTY(QString version READ version)
Q_PROPERTY(EventBus *eventbus READ eventbus FINAL)
Q_PROPERTY(Jellyfin::EventBus *eventbus READ eventbus FINAL)
Q_PROPERTY(Jellyfin::WebSocket *websocket READ websocket FINAL)
Q_PROPERTY(QVariantList supportedCommands READ supportedCommands WRITE setSupportedCommands NOTIFY supportedCommandsChanged)
Q_PROPERTY(Jellyfin::ViewModel::Settings *settings READ settings NOTIFY settingsChanged)

View file

@ -45,9 +45,11 @@ signals:
/**
* @brief The server has requested to display an message to the user
* @param header The header of the message.
* @param message The message to show.
* @param timeout Timeout in MS to show the message. -1: no timeout supplied.
*/
void displayMessage(const QString &message);
void displayMessage(const QString &header, const QString &message, int timeout = -1);
};
}

View file

@ -37,6 +37,19 @@ extern template QDateTime fromJsonValue<QDateTime>(const QJsonValue &source, con
extern template QVariant fromJsonValue<QVariant>(const QJsonValue &source, convertType<QVariant>);
extern template QUuid fromJsonValue<QUuid>(const QJsonValue &source, convertType<QUuid>);
extern template QJsonValue toJsonValue<int>(const int &source, convertType<int>);
extern template QJsonValue toJsonValue<qint64>(const qint64 &source, convertType<qint64>);
extern template QJsonValue toJsonValue<bool>(const bool &source, convertType<bool>);
extern template QJsonValue toJsonValue<QString>(const QString &source, convertType<QString>);
extern template QJsonValue toJsonValue<QStringList>(const QStringList &source, convertType<QStringList>);
extern template QJsonValue toJsonValue<QJsonObject>(const QJsonObject &source, convertType<QJsonObject>);
extern template QJsonValue toJsonValue<double>(const double &source, convertType<double>);
extern template QJsonValue toJsonValue<float>(const float &source, convertType<float>);
extern template QJsonValue toJsonValue<QDateTime>(const QDateTime &source, convertType<QDateTime>);
extern template QJsonValue toJsonValue<QVariant>(const QVariant &source, convertType<QVariant>);
extern template QJsonValue toJsonValue<QUuid>(const QUuid &source, convertType<QUuid>);
extern template QString toString(const QUuid &source, convertType<QUuid>);
extern template QString toString(const qint32 &source, convertType<qint32>);
extern template QString toString(const qint64 &source, convertType<qint64>);

View file

@ -33,9 +33,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include "apiclient.h"
Q_DECLARE_LOGGING_CATEGORY(jellyfinWebSocket);
namespace Jellyfin {
class ApiClient;
namespace DTO {
class UserItemDataDto;
using UserData = UserItemDataDto;

View file

@ -465,7 +465,6 @@ void ApiClient::onUserDataChanged(const QString &itemId, UserData *userData) {
void ApiClient::setAuthenticated(bool authenticated) {
Q_D(ApiClient);
d->authenticated = authenticated;
if (authenticated) d->webSocket->open();
emit authenticatedChanged(authenticated);
}

View file

@ -27,6 +27,7 @@
#include "JellyfinQt/apiclient.h"
#include "JellyfinQt/apimodel.h"
#include "JellyfinQt/eventbus.h"
#include "JellyfinQt/serverdiscoverymodel.h"
#include "JellyfinQt/websocket.h"
#include "JellyfinQt/viewmodel/item.h"

View file

@ -28,7 +28,7 @@ QString uuidToString(const QUuid &source) {
QString str = source.toString();
// Convert {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} (length: 38)
// to xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (lenght: 32)
return QString(str.mid(1, 8) + str.mid(10, 4) + str.mid(15, 4) + str.mid(20, 4) + str.mid(25 + 12));
return QString(str.mid(1, 8) + str.mid(10, 4) + str.mid(15, 4) + str.mid(20, 4) + str.mid(25, 12));
}
QUuid stringToUuid(const QString &source) {
if (source.size() != 32) throw ParseException("Error while trying to parse JSON value as QUid: invalid length");

View file

@ -23,7 +23,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include <JellyfinQt/dto/generalcommandtype.h>
#include <JellyfinQt/dto/useritemdatadto.h>
Q_LOGGING_CATEGORY(jellyfinWebSocket, "jellyfin.websocket");
namespace Jellyfin {
WebSocket::WebSocket(ApiClient *client)
: QObject (client), m_apiClient(client){
connect(&m_webSocket, &QWebSocket::connected, this, &WebSocket::onConnected);
@ -31,11 +34,17 @@ WebSocket::WebSocket(ApiClient *client)
connect(&m_webSocket, static_cast<void (QWebSocket::*)(QAbstractSocket::SocketError)>(&QWebSocket::error),
this, [this](QAbstractSocket::SocketError error) {
Q_UNUSED(error)
qDebug() << "Connection error: " << m_webSocket.errorString();
qCDebug(jellyfinWebSocket) << "Connection error: " << m_webSocket.errorString();
});
connect(&m_webSocket, &QWebSocket::stateChanged, this, &WebSocket::onWebsocketStateChanged);
connect(&m_keepAliveTimer, &QTimer::timeout, this, &WebSocket::sendKeepAlive);
connect(&m_retryTimer, &QTimer::timeout, this, &WebSocket::open);
connect(client, &ApiClient::authenticatedChanged, this, [this](bool isAuthenticated) {
if (isAuthenticated) {
this->m_reconnectAttempt = 0;
this->open();
}
});
}
void WebSocket::open() {
@ -48,7 +57,7 @@ void WebSocket::open() {
connectionUrl.setQuery(query);
m_webSocket.open(connectionUrl);
m_reconnectAttempt++;
qDebug() << "Opening WebSocket connection to " << m_webSocket.requestUrl() << ", connect attempt " << m_reconnectAttempt;
qCDebug(jellyfinWebSocket) << "Opening WebSocket connection to " << m_webSocket.requestUrl() << ", connect attempt " << m_reconnectAttempt;
}
void WebSocket::onConnected() {
@ -66,25 +75,47 @@ void WebSocket::onDisconnected() {
}
void WebSocket::textMessageReceived(const QString &message) {
qDebug() << "WebSocket: message received: " << message;
qCDebug(jellyfinWebSocket) << "message received: " << message;
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
if (doc.isNull() || !doc.isObject()) {
qWarning() << "Malformed message received over WebSocket: parse error or root not an object.";
qCWarning(jellyfinWebSocket()) << "Malformed message received over WebSocket: parse error or root not an object.";
return;
}
QJsonObject messageRoot = doc.object();
if (!messageRoot.contains("MessageType")) {
qWarning() << "Malformed message received over WebSocket: no MessageType set.";
qCWarning(jellyfinWebSocket) << "Malformed message received over WebSocket: no MessageType set.";
return;
}
// Convert the type so we can use it in our enums.
QString messageTypeStr = messageRoot["MessageType"].toString();
QString messageType = messageRoot["MessageType"].toString();
QJsonValue data = messageRoot["Data"];
if (messageTypeStr == QStringLiteral("ForceKeepAlive")) {
if (messageType == QStringLiteral("ForceKeepAlive")) {
setupKeepAlive(data.toInt());
} else if (messageType == QStringLiteral("GeneralCommand")) {
try {
DTO::GeneralCommand command = DTO::GeneralCommand::fromJson(messageRoot["Data"].toObject());
// TODO: move command handling out of here
switch(command.name()) {
case DTO::GeneralCommandType::DisplayMessage:
{
QString header = command.arguments()["Header"].toString("Message from server");
QString text = command.arguments()["Text"].toString("<Empty message>");
int timeout = command.arguments()["TimeoutMs"].toInt(-1);
emit m_apiClient->eventbus()->displayMessage(header, text, timeout);
}
break;
default:
qCDebug(jellyfinWebSocket) << "Unhandled command: " << messageRoot["Data"];
break;
}
} catch(QException &e) {
qCWarning(jellyfinWebSocket()) << "Error while deserializing command: " << e.what();
}
} else {
qDebug() << messageTypeStr;
qCDebug(jellyfinWebSocket) << messageType;
}
bool ok;
/*MessageType messageType = static_cast<MessageType>(QMetaEnum::fromType<WebSocket::MessageType>().keyToValue(messageTypeStr.toLatin1(), &ok));
@ -145,6 +176,6 @@ void WebSocket::sendMessage(MessageType type, QJsonValue data) {
root["Data"] = data;
QString message = QJsonDocument(root).toJson(QJsonDocument::Compact);
m_webSocket.sendTextMessage(message);
qDebug() << "Sent message: " << message;
qCDebug(jellyfinWebSocket) << "Sent message: " << message;
}
}