implement new media interfaces in kodi

This commit is contained in:
Michael Zanetti 2018-11-30 16:11:48 +01:00
parent 71314cafe3
commit fc90d10636
10 changed files with 408 additions and 252 deletions

View File

@ -65,6 +65,12 @@
#include "plugin/device.h"
#include "plugininfo.h"
#include "network/upnp/upnpdiscovery.h"
#include "network/avahi/qtavahiservicebrowser.h"
#include "network/avahi/avahiserviceentry.h"
#include "network/networkaccessmanager.h"
#include <QNetworkRequest>
#include <QNetworkReply>
DevicePluginKodi::DevicePluginKodi()
{
@ -106,6 +112,56 @@ DeviceManager::DeviceSetupStatus DevicePluginKodi::setupDevice(Device *device)
connect(kodi, &Kodi::updateDataReceived, this, &DevicePluginKodi::onSetupFinished);
connect(kodi, &Kodi::playbackStatusChanged, this, &DevicePluginKodi::onPlaybackStatusChanged);
connect(kodi, &Kodi::activePlayerChanged, device, [device](const QString &playerType){
device->setStateValue(kodiPlayerTypeStateTypeId, playerType);
});
connect(kodi, &Kodi::mediaMetadataChanged, device, [this, device](const QString &title, const QString &artist, const QString &collection, const QString &artwork){
device->setStateValue(kodiTitleStateTypeId, title);
device->setStateValue(kodiArtistStateTypeId, artist);
device->setStateValue(kodiCollectionStateTypeId, collection);
QNetworkRequest request;
QHostAddress hostAddr(device->paramValue(kodiDeviceIpParamTypeId).toString());
QString addr;
if (hostAddr.protocol() == QAbstractSocket::IPv4Protocol) {
addr = hostAddr.toString();
} else {
addr = "[" + hostAddr.toString() + "]";
}
request.setUrl(QUrl("http://" + addr + ":8080/jsonrpc"));
qCDebug(dcKodi) << "Prepping file dl" << "http://" + addr + ":" + device->paramValue(kodiDevicePortParamTypeId).toString() + "/jsonrpc";
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QVariantMap map;
map.insert("jsonrpc", "2.0");
map.insert("method", "Files.PrepareDownload");
map.insert("id", QString::number(123));
QVariantMap params;
params.insert("path", artwork);
map.insert("params", params);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(map);
QNetworkReply *reply = hardwareManager()->networkManager()->post(request, jsonDoc.toJson(QJsonDocument::Compact));
connect(reply, &QNetworkReply::finished, device, [device, reply, addr](){
reply->deleteLater();
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll());
device->setStateValue(kodiArtworkStateTypeId, "http://" + addr + ":8080/" + jsonDoc.toVariant().toMap().value("result").toMap().value("details").toMap().value("path").toString());
});
});
connect(kodi, &Kodi::shuffleChanged, device, [device](bool shuffle){
device->setStateValue(kodiShuffleStateTypeId, shuffle);
});
connect(kodi, &Kodi::repeatChanged, device, [device](const QString &repeat){
if (repeat == "one") {
device->setStateValue(kodiRepeatStateTypeId, "One");
} else if (repeat == "all") {
device->setStateValue(kodiRepeatStateTypeId, "All");
} else {
device->setStateValue(kodiRepeatStateTypeId, "None");
}
});
m_kodis.insert(kodi, device);
m_asyncSetups.append(kodi);
@ -127,9 +183,21 @@ DeviceManager::DeviceError DevicePluginKodi::discoverDevices(const DeviceClassId
Q_UNUSED(params)
Q_UNUSED(deviceClassId)
qCDebug(dcKodi) << "Start UPnP search";
UpnpDiscoveryReply *reply = hardwareManager()->upnpDiscovery()->discoverDevices();
connect(reply, &UpnpDiscoveryReply::finished, this, &DevicePluginKodi::onUpnpDiscoveryFinished);
QList<DeviceDescriptor> descriptors;
foreach (const AvahiServiceEntry &avahiEntry, hardwareManager()->avahiBrowser()->serviceEntries()) {
if (avahiEntry.serviceType() == "_xbmc-jsonrpc._tcp") {
qCDebug(dcKodi) << "Zeroconf entry:" << avahiEntry;
DeviceDescriptor descriptor(kodiDeviceClassId, avahiEntry.name(), avahiEntry.hostName() + " (" + avahiEntry.hostAddress().toString() + ")");
ParamList params;
params << Param(kodiDeviceIpParamTypeId, avahiEntry.hostAddress().toString());
params << Param(kodiDevicePortParamTypeId, avahiEntry.port());
descriptor.setParams(params);
descriptors << descriptor;
}
}
if (!descriptors.isEmpty()) {
emit devicesDiscovered(kodiDeviceClassId, descriptors);
}
return DeviceManager::DeviceErrorAsync;
}
@ -144,52 +212,52 @@ DeviceManager::DeviceError DevicePluginKodi::executeAction(Device *device, const
return DeviceManager::DeviceErrorHardwareNotAvailable;
}
int commandId = -1;
if (action.actionTypeId() == kodiShowNotificationActionTypeId) {
kodi->showNotification(action.param(kodiShowNotificationActionMessageParamTypeId).value().toString(), 8000, action.param(kodiShowNotificationActionTypeParamTypeId).value().toString(), action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->showNotification(action.param(kodiShowNotificationActionMessageParamTypeId).value().toString(), 8000, action.param(kodiShowNotificationActionTypeParamTypeId).value().toString());
} else if (action.actionTypeId() == kodiVolumeActionTypeId) {
kodi->setVolume(action.param(kodiVolumeActionVolumeParamTypeId).value().toInt(), action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->setVolume(action.param(kodiVolumeActionVolumeParamTypeId).value().toInt());
} else if (action.actionTypeId() == kodiMuteActionTypeId) {
kodi->setMuted(action.param(kodiMuteActionMuteParamTypeId).value().toBool(), action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->setMuted(action.param(kodiMuteActionMuteParamTypeId).value().toBool());
} else if (action.actionTypeId() == kodiPressButtonActionTypeId) {
kodi->pressButton(action.param(kodiPressButtonActionButtonParamTypeId).value().toString(), action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->pressButton(action.param(kodiPressButtonActionButtonParamTypeId).value().toString());
} else if (action.actionTypeId() == kodiSystemActionTypeId) {
kodi->systemCommand(action.param(kodiSystemActionSystemCommandParamTypeId).value().toString(), action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->systemCommand(action.param(kodiSystemActionSystemCommandParamTypeId).value().toString());
} else if (action.actionTypeId() == kodiVideoLibraryActionTypeId) {
kodi->videoLibrary(action.param(kodiVideoLibraryActionVideoCommandParamTypeId).value().toString(), action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->videoLibrary(action.param(kodiVideoLibraryActionVideoCommandParamTypeId).value().toString());
} else if (action.actionTypeId() == kodiAudioLibraryActionTypeId) {
kodi->audioLibrary(action.param(kodiAudioLibraryActionAudioCommandParamTypeId).value().toString(), action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->audioLibrary(action.param(kodiAudioLibraryActionAudioCommandParamTypeId).value().toString());
} else if(action.actionTypeId() == kodiSkipBackActionTypeId) {
kodi->pressButton("skipprevious", action.id());
return DeviceManager::DeviceErrorAsync;
} else if(action.actionTypeId() == kodiRewindActionTypeId) {
kodi->pressButton("rewind", action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->pressButton("skipprevious");
} else if(action.actionTypeId() == kodiFastRewindActionTypeId) {
commandId = kodi->pressButton("rewind");
} else if(action.actionTypeId() == kodiStopActionTypeId) {
kodi->pressButton("stop", action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->pressButton("stop");
} else if(action.actionTypeId() == kodiPlayActionTypeId) {
kodi->pressButton("play", action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->pressButton("play");
} else if(action.actionTypeId() == kodiPauseActionTypeId) {
kodi->pressButton("pause", action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->pressButton("pause");
} else if(action.actionTypeId() == kodiFastForwardActionTypeId) {
kodi->pressButton("fastforward", action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->pressButton("fastforward");
} else if(action.actionTypeId() == kodiSkipNextActionTypeId) {
kodi->pressButton("skipnext", action.id());
return DeviceManager::DeviceErrorAsync;
commandId = kodi->pressButton("skipnext");
} else if (action.actionTypeId() == kodiShuffleActionTypeId) {
commandId = kodi->setShuffle(action.param(kodiShuffleActionShuffleParamTypeId).value().toBool());
} else if (action.actionTypeId() == kodiRepeatActionTypeId) {
QString repeat = action.param(kodiRepeatActionRepeatParamTypeId).value().toString();
if (repeat == "One") {
commandId = kodi->setRepeat("one");
} else if (repeat == "All") {
commandId = kodi->setRepeat("all");
} else {
commandId = kodi->setRepeat("off");
}
} else {
qWarning(dcKodi()) << "Unhandled action type" << action.actionTypeId();
return DeviceManager::DeviceErrorActionTypeNotFound;
}
return DeviceManager::DeviceErrorActionTypeNotFound;
m_pendingActions.insert(commandId, action.id());
return DeviceManager::DeviceErrorAsync;
}
return DeviceManager::DeviceErrorDeviceClassNotFound;
}
@ -204,43 +272,6 @@ void DevicePluginKodi::onPluginTimer()
}
}
void DevicePluginKodi::onUpnpDiscoveryFinished()
{
qCDebug(dcKodi()) << "Upnp discovery finished";
UpnpDiscoveryReply *reply = static_cast<UpnpDiscoveryReply *>(sender());
if (reply->error() != UpnpDiscoveryReply::UpnpDiscoveryReplyErrorNoError) {
qCWarning(dcKodi()) << "Upnp discovery error" << reply->error();
}
reply->deleteLater();
QList<DeviceDescriptor> deviceDescriptors;
foreach (const UpnpDeviceDescriptor &upnpDescriptor, reply->deviceDescriptors()) {
if (upnpDescriptor.modelName().contains("Kodi")) {
// check if we already found the kodi on this ip
bool alreadyAdded = false;
foreach (const DeviceDescriptor dDescriptor, deviceDescriptors) {
if (dDescriptor.params().paramValue(kodiDeviceIpParamTypeId).toString() == upnpDescriptor.hostAddress().toString()) {
alreadyAdded = true;
break;
}
}
if (alreadyAdded)
continue;
qCDebug(dcKodi) << upnpDescriptor;
DeviceDescriptor deviceDescriptor(kodiDeviceClassId, "Kodi - Media Center", upnpDescriptor.hostAddress().toString());
ParamList params;
params.append(Param(kodiDeviceNameParamTypeId, upnpDescriptor.friendlyName()));
params.append(Param(kodiDeviceIpParamTypeId, upnpDescriptor.hostAddress().toString()));
params.append(Param(kodiDevicePortParamTypeId, 9090));
deviceDescriptor.setParams(params);
deviceDescriptors.append(deviceDescriptor);
}
}
emit devicesDiscovered(kodiDeviceClassId, deviceDescriptors);
}
void DevicePluginKodi::onConnectionChanged()
{
Kodi *kodi = static_cast<Kodi *>(sender());
@ -267,13 +298,12 @@ void DevicePluginKodi::onStateChanged()
device->setStateValue(kodiMuteStateTypeId, kodi->muted());
}
void DevicePluginKodi::onActionExecuted(const ActionId &actionId, const bool &success)
void DevicePluginKodi::onActionExecuted(int actionId, bool success)
{
if (success) {
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorNoError);
} else {
emit actionExecutionFinished(actionId, DeviceManager::DeviceErrorInvalidParameter);
if (!m_pendingActions.contains(actionId)) {
return;
}
emit actionExecutionFinished(m_pendingActions.value(actionId), success ? DeviceManager::DeviceErrorNoError : DeviceManager::DeviceErrorInvalidParameter);
}
void DevicePluginKodi::versionDataReceived(const QVariantMap &data)
@ -304,7 +334,7 @@ void DevicePluginKodi::onSetupFinished(const QVariantMap &data)
emit deviceSetupFinished(device, DeviceManager::DeviceSetupStatusSuccess);
kodi->showNotification("Connected", 2000, "info", ActionId());
kodi->showNotification("Connected", 2000, "info");
}
void DevicePluginKodi::onPlaybackStatusChanged(const QString &playbackStatus)

View File

@ -52,12 +52,13 @@ private:
QHash<Kodi *, Device *> m_kodis;
QList<Kodi *> m_asyncSetups;
QHash<int, ActionId> m_pendingActions;
private slots:
void onPluginTimer();
void onUpnpDiscoveryFinished();
void onConnectionChanged();
void onStateChanged();
void onActionExecuted(const ActionId &actionId, const bool &success);
void onActionExecuted(int actionId, bool success);
void versionDataReceived(const QVariantMap &data);
void onSetupFinished(const QVariantMap &data);

View File

@ -13,7 +13,7 @@
"name": "kodi",
"displayName": "Kodi",
"deviceIcon": "Tv",
"interfaces": ["mediacontroller", "extendedvolumecontroller"],
"interfaces": ["mediaplayer", "extendedmediacontroller", "extendedvolumecontroller", "mediametadataprovider", "shufflerepeat"],
"basicTags": [
"Service",
"Multimedia",
@ -22,13 +22,6 @@
"createMethods": ["user", "discovery"],
"criticalStateTypeId": "09dfbd40-c97c-4a20-9ecd-f80e389a4864",
"paramTypes": [
{
"id": "a704beb1-b0b0-46fc-91fc-3aac01e1a364",
"name": "name",
"displayName": "name",
"type": "QString",
"inputType": "TextLine"
},
{
"id": "1a897065-57c6-49b3-bac9-1e5db27859e5",
"name": "ip",
@ -89,8 +82,69 @@
"displayNameEvent": "playback status changed",
"displayNameAction": "set playback status",
"writable": true
},
{
"id": "0af58b87-4e45-4f0a-9ef2-0ade74c7c22c",
"type": "QString",
"name": "playerType",
"displayName": "Active player type",
"possibleValues": ["audio", "video"],
"displayNameEvent": "Active player changed",
"defaultValue": "audio"
},
{
"id": "f2209fec-cceb-46ad-8189-4caf42166e6b",
"type": "QString",
"name": "title",
"displayName": "Title",
"displayNameEvent": "Title changed",
"defaultValue": ""
},
{
"id": "8cb920a3-3bf1-4231-92d4-8ac27e7b3d65",
"type": "QString",
"name": "artist",
"displayName": "Artist",
"displayNameEvent": "Artist changed",
"defaultValue": ""
},
{
"id": "ce399eec-9f6a-4903-9916-0e90e38b255e",
"type": "QString",
"name": "collection",
"displayName": "Collection",
"displayNameEvent": "Collection changed",
"defaultValue": ""
},
{
"id": "44304c82-c2f6-433b-b62b-815382617d0b",
"type": "QString",
"name": "artwork",
"displayName": "Artwork",
"displayNameEvent": "Artwork changed",
"defaultValue": ""
},
{
"id": "5913aa2a-629d-4de5-bf44-a4a1f130c118",
"type": "bool",
"name": "shuffle",
"displayName": "Shuffle",
"displayNameEvent": "Shuffle changed",
"displayNameAction": "Set shuffle",
"defaultValue": false,
"writable": true
},
{
"id": "bc02c28e-3f5d-4de4-b9b5-c0b1576c6e7e",
"type": "QString",
"name": "repeat",
"displayName": "Repeat",
"displayNameEvent": "Repeat changed",
"displayNameAction": "Set repeat",
"possibleValues": ["None", "One", "All"],
"defaultValue": "None",
"writable": true
}
],
"eventTypes": [
{
@ -117,7 +171,7 @@
},
{
"id": "7e70b47b-7e79-4521-be34-04a3c427e5b1",
"name": "rewind",
"name": "fastRewind",
"displayName": "rewind"
},
{

View File

@ -23,6 +23,7 @@
#include "kodi.h"
#include <QDebug>
#include "extern-plugininfo.h"
#include <QUrl>
Kodi::Kodi(const QHostAddress &hostAddress, const int &port, QObject *parent) :
QObject(parent),
@ -33,14 +34,8 @@ Kodi::Kodi(const QHostAddress &hostAddress, const int &port, QObject *parent) :
connect (m_connection, &KodiConnection::connectionStatusChanged, this, &Kodi::connectionStatusChanged);
m_jsonHandler = new KodiJsonHandler(m_connection, this);
connect(m_jsonHandler, &KodiJsonHandler::volumeChanged, this, &Kodi::onVolumeChanged);
connect(m_jsonHandler, &KodiJsonHandler::actionExecuted, this, &Kodi::actionExecuted);
connect(m_jsonHandler, &KodiJsonHandler::versionDataReceived, this, &Kodi::versionDataReceived);
connect(m_jsonHandler, &KodiJsonHandler::updateDataReceived, this, &Kodi::updateDataReceived);
connect(m_jsonHandler, &KodiJsonHandler::updateDataReceived, this, &Kodi::onUpdateFinished);
connect(m_jsonHandler, &KodiJsonHandler::playbackStatusChanged, this, &Kodi::playbackStatusChanged);
connect(m_jsonHandler, &KodiJsonHandler::activePlayersChanged, this, &Kodi::activePlayersChanged);
connect(m_jsonHandler, &KodiJsonHandler::playerPropertiesReveived, this, &Kodi::playerPropertiesReceived);
connect(m_jsonHandler, &KodiJsonHandler::notificationReceived, this, &Kodi::processNotification);
connect(m_jsonHandler, &KodiJsonHandler::replyReceived, this, &Kodi::processResponse);
}
QHostAddress Kodi::hostAddress() const
@ -58,12 +53,12 @@ bool Kodi::connected() const
return m_connection->connected();
}
void Kodi::setMuted(const bool &muted, const ActionId &actionId)
int Kodi::setMuted(const bool &muted)
{
QVariantMap params;
params.insert("mute", muted);
m_jsonHandler->sendData("Application.SetMute", params, actionId);
return m_jsonHandler->sendData("Application.SetMute", params);
}
bool Kodi::muted() const
@ -71,12 +66,12 @@ bool Kodi::muted() const
return m_muted;
}
void Kodi::setVolume(const int &volume, const ActionId &actionId)
int Kodi::setVolume(const int &volume)
{
QVariantMap params;
params.insert("volume", volume);
m_jsonHandler->sendData("Application.SetVolume", params, actionId);
return m_jsonHandler->sendData("Application.SetVolume", params);
}
int Kodi::volume() const
@ -84,7 +79,23 @@ int Kodi::volume() const
return m_volume;
}
void Kodi::showNotification(const QString &message, const int &displayTime, const QString &notificationType, const ActionId &actionId)
int Kodi::setShuffle(bool shuffle)
{
QVariantMap params;
params.insert("playerid", m_activePlayer);
params.insert("shuffle", shuffle);
return m_jsonHandler->sendData("Player.SetShuffle", params);
}
int Kodi::setRepeat(const QString &repeat)
{
QVariantMap params;
params.insert("playerid", m_activePlayer);
params.insert("repeat", repeat);
return m_jsonHandler->sendData("Player.SetRepeat", params);
}
int Kodi::showNotification(const QString &message, const int &displayTime, const QString &notificationType)
{
QVariantMap params;
params.insert("title", "nymea notification");
@ -92,17 +103,17 @@ void Kodi::showNotification(const QString &message, const int &displayTime, cons
params.insert("displaytime", displayTime);
params.insert("image", notificationType);
m_jsonHandler->sendData("GUI.ShowNotification", params, actionId);
return m_jsonHandler->sendData("GUI.ShowNotification", params);
}
void Kodi::pressButton(const QString &button, const ActionId &actionId)
int Kodi::pressButton(const QString &button)
{
QVariantMap params;
params.insert("action", button);
m_jsonHandler->sendData("Input.ExecuteAction", params, actionId);
return m_jsonHandler->sendData("Input.ExecuteAction", params);
}
void Kodi::systemCommand(const QString &command, const ActionId &actionId)
int Kodi::systemCommand(const QString &command)
{
QString method;
if (command == "hibernate") {
@ -117,10 +128,10 @@ void Kodi::systemCommand(const QString &command, const ActionId &actionId)
// already checkt with allowed values
}
m_jsonHandler->sendData("System." + method, QVariantMap(), actionId);
return m_jsonHandler->sendData("System." + method, QVariantMap());
}
void Kodi::videoLibrary(const QString &command, const ActionId &actionId)
int Kodi::videoLibrary(const QString &command)
{
QString method;
if (command == "scan") {
@ -131,10 +142,10 @@ void Kodi::videoLibrary(const QString &command, const ActionId &actionId)
// already checkt with allowed values
}
m_jsonHandler->sendData("VideoLibrary." + method, QVariantMap(), actionId);
return m_jsonHandler->sendData("VideoLibrary." + method, QVariantMap());
}
void Kodi::audioLibrary(const QString &command, const ActionId &actionId)
int Kodi::audioLibrary(const QString &command)
{
QString method;
if (command == "scan") {
@ -145,7 +156,7 @@ void Kodi::audioLibrary(const QString &command, const ActionId &actionId)
// already checkt with allowed values
}
m_jsonHandler->sendData("AudioLibrary." + method, QVariantMap(), actionId);
return m_jsonHandler->sendData("AudioLibrary." + method, QVariantMap());
}
void Kodi::update()
@ -158,15 +169,15 @@ void Kodi::update()
properties.append("version");
params.insert("properties", properties);
m_jsonHandler->sendData("Application.GetProperties", params, ActionId());
m_jsonHandler->sendData("Application.GetProperties", params);
params.clear();
m_jsonHandler->sendData("Player.GetActivePlayers", params, ActionId());
m_jsonHandler->sendData("Player.GetActivePlayers", params);
}
void Kodi::checkVersion()
{
m_jsonHandler->sendData("JSONRPC.Version", QVariantMap(), ActionId());
m_jsonHandler->sendData("JSONRPC.Version", QVariantMap());
}
void Kodi::connectKodi()
@ -202,29 +213,162 @@ void Kodi::onUpdateFinished(const QVariantMap &data)
void Kodi::activePlayersChanged(const QVariantList &data)
{
qCDebug(dcKodi()) << "active players changed" << data.count();
qCDebug(dcKodi()) << "active players changed" << data.count() << data;
m_activePlayerCount = data.count();
if (m_activePlayerCount == 0) {
emit playbackStatusChanged("Stopped");
onPlaybackStatusChanged("Stopped");
return;
}
int activePlayer = data.first().toMap().value("playerid").toInt();
QVariantMap params;
params.insert("playerid", activePlayer);
QVariantList properties;
properties.append("speed");
params.insert("properties", properties);
m_jsonHandler->sendData("Player.GetProperties", params, ActionId());
m_activePlayer = data.first().toMap().value("playerid").toInt();
qCDebug(dcKodi) << "Active Player changed:" << m_activePlayer << data.first().toMap().value("type").toString();
emit activePlayerChanged(data.first().toMap().value("type").toString());
updatePlayerProperties();
}
void Kodi::playerPropertiesReceived(const QVariantMap &properties)
{
qCDebug(dcKodi()) << "player props received" << properties;
if (m_activePlayerCount > 0) {
if (properties.value("speed").toDouble() > 0) {
emit playbackStatusChanged("Playing");
onPlaybackStatusChanged("Playing");
} else {
emit playbackStatusChanged("Paused");
onPlaybackStatusChanged("Paused");
}
}
emit shuffleChanged(properties.value("shuffled").toBool());
emit repeatChanged(properties.value("repeat").toString());
}
void Kodi::mediaMetaDataReceived(const QVariantMap &data)
{
QVariantMap item = data.value("item").toMap();
QString title = item.value("title").toString();
QString artist;
QString collection;
if (item.value("type").toString() == "song") {
artist = !item.value("artist").toList().isEmpty() ? item.value("artist").toList().first().toString() : "";
collection = item.value("album").toString();
} else if (item.value("type").toString() == "episode") {
collection = item.value("showtitle").toString();
} else if (item.value("type").toString() == "unknown") {
artist = item.value("channel").toString();
collection = item.value("showtitle").toString();
} else if (item.value("type").toString() == "channel") {
artist = item.value("channel").toString();
collection = item.value("showtitle").toString();
} else if (item.value("type").toString() == "movie") {
artist = item.value("director").toStringList().join(", ");
collection = item.value("year").toString();
}
QString artwork = item.value("thumbnail").toString();
if (artwork.isEmpty()) {
artwork = item.value("fanart").toString();
}
qCDebug(dcKodi) << "title:" << title << artwork;
emit mediaMetadataChanged(title, artist, collection, artwork);
}
void Kodi::onPlaybackStatusChanged(const QString &playbackState)
{
if (playbackState != "Stopped") {
updateMetadata();
} else {
emit mediaMetadataChanged(QString(), QString(), QString(), QString());
}
emit playbackStatusChanged(playbackState);
}
void Kodi::processNotification(const QString &method, const QVariantMap &params)
{
qCDebug(dcKodi) << "got notification" << method << params;
if (method == "Application.OnVolumeChanged") {
QVariantMap data = params.value("data").toMap();
onVolumeChanged(data.value("volume").toInt(), data.value("muted").toBool());
} else if (method == "Player.OnPlay" || method == "Player.OnResume") {
emit activePlayersChanged(QVariantList() << params.value("data").toMap().value("player"));
onPlaybackStatusChanged("Playing");
} else if (method == "Player.OnPause") {
emit playbackStatusChanged("Paused");
} else if (method == "Player.OnStop") {
emit playbackStatusChanged("Stopped");
emit activePlayersChanged(QVariantList());
}
}
void Kodi::processResponse(int id, const QString &method, const QVariantMap &response)
{
qCDebug(dcKodi) << "response received:" << method << response;
if (response.contains("error")) {
//qCDebug(dcKodi) << QJsonDocument::fromVariant(response).toJson();
qCWarning(dcKodi) << "got error response for request " << method << ":" << response.value("error").toMap().value("message").toString();
emit actionExecuted(id, false);
return;
}
if (method == "Application.GetProperties") {
//qCDebug(dcKodi) << "got update response" << reply.method();
emit updateDataReceived(response.value("result").toMap());
return;
}
if (method == "JSONRPC.Version") {
qCDebug(dcKodi) << "got version response" << method;
emit versionDataReceived(response.value("result").toMap());
return;
}
if (method == "Player.GetActivePlayers") {
qCDebug(dcKodi) << "Active players changed" << response;
emit activePlayersChanged(response.value("result").toList());
return;
}
if (method == "Player.GetProperties") {
qCDebug(dcKodi) << "Player properties received" << response;
playerPropertiesReceived(response.value("result").toMap());
return;
}
if (method == "Player.GetItem") {
qCDebug(dcKodi) << "Played item received" << response;
emit mediaMetaDataReceived(response.value("result").toMap());
return;
}
if (method == "Player.SetShuffle" || method == "Player.SetRepeat") {
updatePlayerProperties();
}
emit actionExecuted(id, true);
qCDebug(dcKodi()) << "unhandled reply" << method << response;
}
void Kodi::updatePlayerProperties()
{
QVariantMap params;
params.insert("playerid", m_activePlayer);
QVariantList properties;
properties << "speed" << "shuffled" << "repeat";
params.insert("properties", properties);
m_jsonHandler->sendData("Player.GetProperties", params);
}
void Kodi::updateMetadata()
{
QVariantMap params;
params.insert("playerid", m_activePlayer);
QVariantList fields;
fields << "title" << "artist" << "album" << "director" << "thumbnail" << "showtitle" << "fanart" << "channel" << "year";
params.insert("properties", fields);
m_jsonHandler->sendData("Player.GetItem", params);
}

View File

@ -34,7 +34,7 @@ class Kodi : public QObject
Q_OBJECT
public:
explicit Kodi(const QHostAddress &hostAddress, const int &port = 9090, QObject *parent = 0);
explicit Kodi(const QHostAddress &hostAddress, const int &port = 9090, QObject *parent = nullptr);
QHostAddress hostAddress() const;
int port() const;
@ -42,18 +42,21 @@ public:
bool connected() const;
// propertys
void setMuted(const bool &muted, const ActionId &actionId);
int setMuted(const bool &muted);
bool muted() const;
void setVolume(const int &volume, const ActionId &actionId);
int setVolume(const int &volume);
int volume() const;
int setShuffle(bool shuffle);
int setRepeat(const QString &repeat);
// actions
void showNotification(const QString &message, const int &displayTime, const QString &notificationType, const ActionId &actionId);
void pressButton(const QString &button, const ActionId &actionId);
void systemCommand(const QString &command, const ActionId &actionId);
void videoLibrary(const QString &command, const ActionId &actionId);
void audioLibrary(const QString &command, const ActionId &actionId);
int showNotification(const QString &message, const int &displayTime, const QString &notificationType);
int pressButton(const QString &button);
int systemCommand(const QString &command);
int videoLibrary(const QString &command);
int audioLibrary(const QString &command);
void update();
void checkVersion();
@ -61,26 +64,40 @@ public:
void connectKodi();
void disconnectKodi();
private:
KodiConnection *m_connection;
KodiJsonHandler *m_jsonHandler;
bool m_muted;
int m_volume;
int m_activePlayerCount = 0; // if it's > 0, there is something playing (either music or video or slideshow)
signals:
void connectionStatusChanged();
void stateChanged();
void actionExecuted(const ActionId &actionId, const bool &success);
void activePlayerChanged(const QString &playerType);
void actionExecuted(int actionId, const bool &success);
void updateDataReceived(const QVariantMap &data);
void versionDataReceived(const QVariantMap &data);
void playbackStatusChanged(const QString &playbackState);
void mediaMetadataChanged(const QString &title, const QString &artist, const QString &collection, const QString &artwork);
void shuffleChanged(bool shuffle);
void repeatChanged(const QString &repeat);
private slots:
void onVolumeChanged(const int &volume, const bool &muted);
void onUpdateFinished(const QVariantMap &data);
void activePlayersChanged(const QVariantList &data);
void playerPropertiesReceived(const QVariantMap &properties);
void mediaMetaDataReceived(const QVariantMap &data);
void onPlaybackStatusChanged(const QString &plabackState);
void processNotification(const QString &method, const QVariantMap &params);
void processResponse(int id, const QString &method, const QVariantMap &response);
void updatePlayerProperties();
void updateMetadata();
private:
KodiConnection *m_connection;
KodiJsonHandler *m_jsonHandler;
bool m_muted;
int m_volume;
int m_activePlayerCount = 0; // if it's > 0, there is something playing (either music or video or slideshow)
int m_activePlayer = -1;
};
#endif // KODI_H

View File

@ -32,7 +32,7 @@ class KodiConnection : public QObject
{
Q_OBJECT
public:
explicit KodiConnection(const QHostAddress &hostAddress, const int &port = 9090, QObject *parent = 0);
explicit KodiConnection(const QHostAddress &hostAddress, const int &port = 9090, QObject *parent = nullptr);
void connectKodi();
void disconnectKodi();
@ -62,6 +62,8 @@ signals:
public slots:
void sendData(const QByteArray &message);
};
#endif // KODICONNECTION_H

View File

@ -33,82 +33,22 @@ KodiJsonHandler::KodiJsonHandler(KodiConnection *connection, QObject *parent) :
connect(m_connection, &KodiConnection::dataReady, this, &KodiJsonHandler::processResponse);
}
void KodiJsonHandler::sendData(const QString &method, const QVariantMap &params, const ActionId &actionId)
int KodiJsonHandler::sendData(const QString &method, const QVariantMap &params)
{
m_id++;
QVariantMap package;
package.insert("id", m_id);
package.insert("method", method);
package.insert("params", params);
package.insert("jsonrpc", "2.0");
m_replys.insert(m_id, KodiReply(method, params, actionId));
m_replys.insert(m_id, KodiReply(method, params));
QJsonDocument jsonDoc = QJsonDocument::fromVariant(package);
m_connection->sendData(jsonDoc.toJson());
//qCDebug(dcKodi) << "sending data" << jsonDoc.toJson();
m_id++;
}
void KodiJsonHandler::processNotification(const QString &method, const QVariantMap &params)
{
qCDebug(dcKodi) << "got notification" << method;
if (method == "Application.OnVolumeChanged") {
QVariantMap data = params.value("data").toMap();
emit volumeChanged(data.value("volume").toInt(), data.value("muted").toBool());
} else if (method == "Player.OnPlay" || method == "Player.OnResume") {
emit playbackStatusChanged("Playing");
} else if (method == "Player.OnPause") {
emit playbackStatusChanged("Paused");
} else if (method == "Player.OnStop") {
emit playbackStatusChanged("Stopped");
}
}
void KodiJsonHandler::processActionResponse(const KodiReply &reply, const QVariantMap &response)
{
if (response.contains("error")) {
//qCDebug(dcKodi) << QJsonDocument::fromVariant(response).toJson();
qCWarning(dcKodi) << "got error response for action" << reply.method() << ":" << response.value("error").toMap().value("message").toString();
emit actionExecuted(reply.actionId(), false);
} else {
emit actionExecuted(reply.actionId(), true);
}
}
void KodiJsonHandler::processRequestResponse(const KodiReply &reply, const QVariantMap &response)
{
if (response.contains("error")) {
//qCDebug(dcKodi) << QJsonDocument::fromVariant(response).toJson();
qCWarning(dcKodi) << "got error response for request " << reply.method() << ":" << response.value("error").toMap().value("message").toString();
return;
}
if (reply.method() == "Application.GetProperties") {
//qCDebug(dcKodi) << "got update response" << reply.method();
emit updateDataReceived(response.value("result").toMap());
return;
}
if (reply.method() == "JSONRPC.Version") {
qCDebug(dcKodi) << "got version response" << reply.method();
emit versionDataReceived(response.value("result").toMap());
return;
}
if (reply.method() == "Player.GetActivePlayers") {
qCDebug(dcKodi) << "Active players changed" << response;
emit activePlayersChanged(response.value("result").toList());
return;
}
if (reply.method() == "Player.GetProperties") {
qCDebug(dcKodi) << "Player properties received" << response;
emit playerPropertiesReveived(response.value("result").toMap());
return;
}
qCDebug(dcKodi()) << "unhandled reply" << reply.method() << response;
return m_id;
}
void KodiJsonHandler::processResponse(const QByteArray &data)
@ -138,18 +78,12 @@ void KodiJsonHandler::processResponse(const QByteArray &data)
qCWarning(dcKodi) << "method missing in message" << data;
}
processNotification(message.value("method").toString(), message.value("params").toMap());
emit notificationReceived(message.value("method").toString(), message.value("params").toMap());
return;
}
int id = message.value("id").toInt();
KodiReply reply = m_replys.take(id);
// check if this message is a response to an action call
if (reply.actionId() != ActionId()) {
processActionResponse(reply, message);
return;
}
processRequestResponse(reply, message);
emit replyReceived(id, reply.method(), message);
}

View File

@ -35,32 +35,22 @@ class KodiJsonHandler : public QObject
{
Q_OBJECT
public:
explicit KodiJsonHandler(KodiConnection *connection = 0, QObject *parent = 0);
explicit KodiJsonHandler(KodiConnection *connection, QObject *parent = nullptr);
void sendData(const QString &method, const QVariantMap &params, const ActionId &actionId);
int sendData(const QString &method, const QVariantMap &params);
signals:
void notificationReceived(const QString &method, const QVariantMap &params);
void replyReceived(int id, const QString &method, const QVariantMap &params);
private slots:
void processResponse(const QByteArray &data);
private:
KodiConnection *m_connection;
int m_id;
QHash<int, KodiReply> m_replys;
void processNotification(const QString &method, const QVariantMap &params);
void processActionResponse(const KodiReply &reply, const QVariantMap &response);
void processRequestResponse(const KodiReply &reply, const QVariantMap &response);
signals:
void volumeChanged(const int &volume, const bool &muted);
void actionExecuted(const ActionId &actionId, const bool &success);
void updateDataReceived(const QVariantMap &data);
void versionDataReceived(const QVariantMap &data);
void activePlayersChanged(const QVariantList &data);
void playerPropertiesReveived(const QVariantMap &properties);
void playbackStatusChanged(const QString &playbackStatus);
private slots:
void processResponse(const QByteArray &data);
};
#endif // KODIJSONHANDLER_H

View File

@ -26,23 +26,12 @@ KodiReply::KodiReply()
{
}
KodiReply::KodiReply(const QString &method, const QVariantMap &params, const ActionId &actionId) :
KodiReply::KodiReply(const QString &method, const QVariantMap &params) :
m_method(method),
m_params(params),
m_actionId(actionId)
m_params(params)
{
}
void KodiReply::setActionId(const ActionId &actionId)
{
m_actionId = actionId;
}
ActionId KodiReply::actionId() const
{
return m_actionId;
}
void KodiReply::setMethod(const QString &method)
{
m_method = method;

View File

@ -32,10 +32,7 @@ class KodiReply
{
public:
KodiReply();
KodiReply(const QString &method, const QVariantMap &params, const ActionId &actionId = ActionId());
void setActionId(const ActionId &actionId);
ActionId actionId() const;
KodiReply(const QString &method, const QVariantMap &params);
void setMethod(const QString &method);
QString method() const;
@ -46,8 +43,6 @@ public:
private:
QString m_method;
QVariantMap m_params;
ActionId m_actionId;
};
#endif // KODIREPLY_H