mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2024-11-22 01:05:17 +00:00
Added basis for WebSocket connection
This commit is contained in:
parent
5576e53656
commit
97429ff6ec
|
@ -12,7 +12,7 @@
|
||||||
# The name of your application
|
# The name of your application
|
||||||
TARGET = harbour-sailfin
|
TARGET = harbour-sailfin
|
||||||
|
|
||||||
QT += multimedia
|
QT += multimedia websockets
|
||||||
|
|
||||||
CONFIG += sailfishapp c++11
|
CONFIG += sailfishapp c++11
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ SOURCES += \
|
||||||
src/jellyfinapimodel.cpp \
|
src/jellyfinapimodel.cpp \
|
||||||
src/jellyfindeviceprofile.cpp \
|
src/jellyfindeviceprofile.cpp \
|
||||||
src/jellyfinmediasource.cpp \
|
src/jellyfinmediasource.cpp \
|
||||||
|
src/jellyfinwebsocket.cpp \
|
||||||
src/serverdiscoverymodel.cpp
|
src/serverdiscoverymodel.cpp
|
||||||
|
|
||||||
DISTFILES += \
|
DISTFILES += \
|
||||||
|
@ -90,4 +91,5 @@ HEADERS += \
|
||||||
src/jellyfinapimodel.h \
|
src/jellyfinapimodel.h \
|
||||||
src/jellyfindeviceprofile.h \
|
src/jellyfindeviceprofile.h \
|
||||||
src/jellyfinmediasource.h \
|
src/jellyfinmediasource.h \
|
||||||
|
src/jellyfinwebsocket.h \
|
||||||
src/serverdiscoverymodel.h
|
src/serverdiscoverymodel.h
|
||||||
|
|
|
@ -22,6 +22,7 @@ PkgConfigBR:
|
||||||
- Qt5Core
|
- Qt5Core
|
||||||
- Qt5Qml
|
- Qt5Qml
|
||||||
- Qt5Quick
|
- Qt5Quick
|
||||||
|
- Qt5WebSockets
|
||||||
|
|
||||||
# Build dependencies without a pkgconfig setup can be listed here
|
# Build dependencies without a pkgconfig setup can be listed here
|
||||||
# PkgBR:
|
# PkgBR:
|
||||||
|
@ -30,6 +31,7 @@ PkgConfigBR:
|
||||||
# Runtime dependencies which are not automatically detected
|
# Runtime dependencies which are not automatically detected
|
||||||
Requires:
|
Requires:
|
||||||
- sailfishsilica-qt5 >= 0.10.9
|
- sailfishsilica-qt5 >= 0.10.9
|
||||||
|
- qt5-qtdeclarative-import-xmllistmodel
|
||||||
|
|
||||||
# All installed files
|
# All installed files
|
||||||
Files:
|
Files:
|
||||||
|
|
|
@ -21,7 +21,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
namespace Jellyfin {
|
namespace Jellyfin {
|
||||||
ApiClient::ApiClient(QObject *parent)
|
ApiClient::ApiClient(QObject *parent)
|
||||||
: QObject(parent) {
|
: QObject(parent),
|
||||||
|
m_webSocket(new WebSocket(this)) {
|
||||||
m_deviceName = QHostInfo::localHostName();
|
m_deviceName = QHostInfo::localHostName();
|
||||||
m_deviceId = QUuid::createUuid().toString(); // TODO: make this not random?
|
m_deviceId = QUuid::createUuid().toString(); // TODO: make this not random?
|
||||||
m_credManager = CredentialsManager::newInstance(this);
|
m_credManager = CredentialsManager::newInstance(this);
|
||||||
|
@ -268,4 +269,10 @@ void ApiClient::defaultNetworkErrorHandler(QNetworkReply::NetworkError error) {
|
||||||
}
|
}
|
||||||
rep->deleteLater();
|
rep->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiClient::setAuthenticated(bool authenticated) {
|
||||||
|
this->m_authenticated = authenticated;
|
||||||
|
if (authenticated) m_webSocket->open();
|
||||||
|
emit authenticatedChanged(authenticated);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
#include "credentialmanager.h"
|
#include "credentialmanager.h"
|
||||||
#include "jellyfindeviceprofile.h"
|
#include "jellyfindeviceprofile.h"
|
||||||
|
#include "jellyfinwebsocket.h"
|
||||||
|
|
||||||
namespace Jellyfin {
|
namespace Jellyfin {
|
||||||
class MediaSource;
|
class MediaSource;
|
||||||
|
class WebSocket;
|
||||||
/**
|
/**
|
||||||
* @brief An Api client for Jellyfin. Handles requests and authentication.
|
* @brief An Api client for Jellyfin. Handles requests and authentication.
|
||||||
*
|
*
|
||||||
|
@ -67,6 +69,7 @@ class MediaSource;
|
||||||
*/
|
*/
|
||||||
class ApiClient : public QObject {
|
class ApiClient : public QObject {
|
||||||
friend class MediaSource;
|
friend class MediaSource;
|
||||||
|
friend class WebSocket;
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ApiClient(QObject *parent = nullptr);
|
explicit ApiClient(QObject *parent = nullptr);
|
||||||
|
@ -194,6 +197,7 @@ private:
|
||||||
/*
|
/*
|
||||||
* State information
|
* State information
|
||||||
*/
|
*/
|
||||||
|
WebSocket *m_webSocket;
|
||||||
CredentialsManager * m_credManager;
|
CredentialsManager * m_credManager;
|
||||||
QString m_token;
|
QString m_token;
|
||||||
QString m_deviceName;
|
QString m_deviceName;
|
||||||
|
@ -212,10 +216,8 @@ private:
|
||||||
* Setters
|
* Setters
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void setAuthenticated(bool authenticated) {
|
void setAuthenticated(bool authenticated);
|
||||||
this->m_authenticated = authenticated;
|
|
||||||
emit authenticatedChanged(authenticated);
|
|
||||||
}
|
|
||||||
void setUserId(QString userId) {
|
void setUserId(QString userId) {
|
||||||
this->m_userId = userId;
|
this->m_userId = userId;
|
||||||
emit userIdChanged(userId);
|
emit userIdChanged(userId);
|
||||||
|
|
108
src/jellyfinwebsocket.cpp
Normal file
108
src/jellyfinwebsocket.cpp
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
Sailfin: a Jellyfin client written using Qt
|
||||||
|
Copyright (C) 2020 Chris Josten
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include "jellyfinwebsocket.h"
|
||||||
|
|
||||||
|
namespace Jellyfin {
|
||||||
|
WebSocket::WebSocket(ApiClient *client)
|
||||||
|
: QObject (client), m_apiClient(client){
|
||||||
|
connect(&m_webSocket, &QWebSocket::connected, this, &WebSocket::onConnected);
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocket::open() {
|
||||||
|
QUrlQuery query;
|
||||||
|
query.addQueryItem("api_key", m_apiClient->token());
|
||||||
|
query.addQueryItem("deviceId", m_apiClient->m_deviceId);
|
||||||
|
QUrl connectionUrl(m_apiClient->baseUrl());
|
||||||
|
connectionUrl.setScheme(connectionUrl.scheme() == "http" ? "ws" : "wss");
|
||||||
|
connectionUrl.setPath("/socket");
|
||||||
|
connectionUrl.setQuery(query);
|
||||||
|
m_webSocket.open(connectionUrl);
|
||||||
|
qDebug() << "Opening WebSocket connection to " << m_webSocket.requestUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocket::onConnected() {
|
||||||
|
connect(&m_webSocket, &QWebSocket::textMessageReceived, this, &WebSocket::textMessageReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocket::textMessageReceived(const QString &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.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QJsonObject messageRoot = doc.object();
|
||||||
|
if (!messageRoot.contains("MessageType") || !messageRoot.contains("Data")) {
|
||||||
|
qWarning() << "Malformed message received over WebSocket: no MessageType and Data set.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the type so we can use it in our enums.
|
||||||
|
QString messageTypeStr = messageRoot["MessageType"].toString();
|
||||||
|
bool ok;
|
||||||
|
MessageType messageType = static_cast<MessageType>(QMetaEnum::fromType<WebSocket::MessageType>().keyToValue(messageTypeStr.toLatin1(), &ok));
|
||||||
|
if (!ok) {
|
||||||
|
qWarning() << "Unknown message arrived: " << messageTypeStr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonValue data = messageRoot["Data"];
|
||||||
|
qDebug() << "Received message: " << messageTypeStr;
|
||||||
|
|
||||||
|
switch (messageType) {
|
||||||
|
case ForceKeepAlive:
|
||||||
|
setupKeepAlive(data.toInt(-1));
|
||||||
|
break;
|
||||||
|
case KeepAlive:
|
||||||
|
//TODO: do something?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocket::sendKeepAlive() {
|
||||||
|
sendMessage(KeepAlive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocket::setupKeepAlive(int data) {
|
||||||
|
// Data is timeout in seconds, we want to send a keepalive at half the timeout
|
||||||
|
m_keepAliveTimer.setInterval(data * 500);
|
||||||
|
m_keepAliveTimer.setSingleShot(false);
|
||||||
|
connect(&m_keepAliveTimer, &QTimer::timeout, this, &WebSocket::sendKeepAlive);
|
||||||
|
m_keepAliveTimer.start();
|
||||||
|
sendKeepAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WebSocket::generateMessageId() {
|
||||||
|
return QUuid::createUuid().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocket::sendMessage(MessageType type, QJsonValue data) {
|
||||||
|
QJsonObject root;
|
||||||
|
root["MessageType"] = QVariant::fromValue(type).toString();
|
||||||
|
root["Data"] = data;
|
||||||
|
QString message = QJsonDocument(root).toJson(QJsonDocument::Compact);
|
||||||
|
m_webSocket.sendTextMessage(message);
|
||||||
|
qDebug() << "Sent message: " << message;
|
||||||
|
}
|
||||||
|
}
|
76
src/jellyfinwebsocket.h
Normal file
76
src/jellyfinwebsocket.h
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
Sailfin: a Jellyfin client written using Qt
|
||||||
|
Copyright (C) 2020 Chris Josten
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#ifndef JELLYFIN_WEBSOCKET_H
|
||||||
|
#define JELLYFIN_WEBSOCKET_H
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
|
#include <QtWebSockets/QWebSocket>
|
||||||
|
|
||||||
|
#include "jellyfinapiclient.h"
|
||||||
|
|
||||||
|
namespace Jellyfin {
|
||||||
|
class ApiClient;
|
||||||
|
class WebSocket : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief WebSocket creates a webSocket for a Jellyfin server to handle real time updates.
|
||||||
|
* @param client The client to create the socket for.
|
||||||
|
*
|
||||||
|
* The socket will automatically set the ApiClient to its parent.
|
||||||
|
*/
|
||||||
|
explicit WebSocket(ApiClient *client);
|
||||||
|
enum MessageType {
|
||||||
|
ForceKeepAlive,
|
||||||
|
KeepAlive
|
||||||
|
};
|
||||||
|
Q_ENUM(MessageType)
|
||||||
|
public slots:
|
||||||
|
void open();
|
||||||
|
private slots:
|
||||||
|
void textMessageReceived(const QString &message);
|
||||||
|
void onConnected();
|
||||||
|
|
||||||
|
void sendKeepAlive();
|
||||||
|
signals:
|
||||||
|
void commandReceived(QString arts, QVariantMap args);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ApiClient *m_apiClient;
|
||||||
|
QWebSocket m_webSocket;
|
||||||
|
|
||||||
|
QTimer m_keepAliveTimer;
|
||||||
|
|
||||||
|
|
||||||
|
void setupKeepAlive(int data);
|
||||||
|
void sendMessage(MessageType type, QJsonValue data = QJsonValue());
|
||||||
|
QString generateMessageId();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // JELLYFIN_WEBSOCKET_H
|
Loading…
Reference in a new issue