From 37909b1d2f2b181f16470c782913614e7081bcd1 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sat, 20 Jul 2019 01:41:07 +0200 Subject: [PATCH] Make update/clean library browserItemActions --- kodi/devicepluginkodi.cpp | 104 ++++++++++++--- kodi/devicepluginkodi.h | 4 +- kodi/devicepluginkodi.json | 63 ++++------ kodi/kodi.cpp | 250 +++++++++++++++++++++++++------------ kodi/kodi.h | 9 +- 5 files changed, 296 insertions(+), 134 deletions(-) diff --git a/kodi/devicepluginkodi.cpp b/kodi/devicepluginkodi.cpp index 30eadd35..6799ff93 100644 --- a/kodi/devicepluginkodi.cpp +++ b/kodi/devicepluginkodi.cpp @@ -76,6 +76,7 @@ Device::DeviceSetupStatus DevicePluginKodi::setupDevice(Device *device) connect(kodi, &Kodi::playbackStatusChanged, this, &DevicePluginKodi::onPlaybackStatusChanged); connect(kodi, &Kodi::browseResult, this, &DevicePluginKodi::browseRequestFinished); connect(kodi, &Kodi::browserItemResult, this, &DevicePluginKodi::browserItemRequestFinished); + connect(kodi, &Kodi::browserItemActionExecuted, this, &DevicePluginKodi::onBrowserItemActionExecuted); connect(kodi, &Kodi::activePlayerChanged, device, [device](const QString &playerType){ device->setStateValue(kodiPlayerTypeStateTypeId, playerType); @@ -94,8 +95,9 @@ Device::DeviceSetupStatus DevicePluginKodi::setupDevice(Device *device) } else { addr = "[" + hostAddr.toString() + "]"; } + QString port = device->paramValue(kodiDeviceHttpPortParamTypeId).toString(); - request.setUrl(QUrl("http://" + addr + ":8080/jsonrpc")); + request.setUrl(QUrl(QString("http://%1:%2/jsonrpc").arg(addr).arg(port))); qCDebug(dcKodi) << "Prepping file dl" << "http://" + addr + ":" + device->paramValue(kodiDevicePortParamTypeId).toString() + "/jsonrpc"; request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QVariantMap map; @@ -107,10 +109,10 @@ Device::DeviceSetupStatus DevicePluginKodi::setupDevice(Device *device) 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](){ + connect(reply, &QNetworkReply::finished, device, [device, reply, addr, port](){ reply->deleteLater(); QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll()); - QString fileUrl = "http://" + addr + ":8080/" + jsonDoc.toVariant().toMap().value("result").toMap().value("details").toMap().value("path").toString(); + QString fileUrl = "http://" + addr + ":" + port + "/" + jsonDoc.toVariant().toMap().value("result").toMap().value("details").toMap().value("path").toString(); qCDebug(dcKodi()) << "DL result:" << jsonDoc.toJson(); qCDebug(dcKodi()) << "Resolved url:" << fileUrl; device->setStateValue(kodiArtworkStateTypeId, fileUrl); @@ -152,19 +154,68 @@ Device::DeviceError DevicePluginKodi::discoverDevices(const DeviceClassId &devic Q_UNUSED(deviceClassId) ZeroConfServiceBrowser *serviceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_xbmc-jsonrpc._tcp"); - QTimer::singleShot(5000, this, [this, serviceBrowser](){ - QList descriptors; + ZeroConfServiceBrowser *httpServiceBrowser = hardwareManager()->zeroConfController()->createServiceBrowser("_http._tcp"); + QTimer::singleShot(5000, this, [this, serviceBrowser, httpServiceBrowser](){ + QHash descriptors; + foreach (const ZeroConfServiceEntry avahiEntry, serviceBrowser->serviceEntries()) { + + QString uuid; + foreach (const QString &txt, avahiEntry.txt()) { + if (txt.startsWith("uuid")) { + uuid = txt.split("=").last(); + break; + } + } + + if (descriptors.contains(uuid)) { + // Might appear multiple times, IPv4 and IPv6 + continue; + } + 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()); + params << Param(kodiDeviceUuidParamTypeId, uuid); descriptor.setParams(params); - descriptors << descriptor; + + Devices existing = myDevices().filterByParam(kodiDeviceUuidParamTypeId, uuid); + if (existing.count() > 0) { + descriptor.setDeviceId(existing.first()->id()); + } + + descriptors.insert(uuid, descriptor); } - emit devicesDiscovered(kodiDeviceClassId, descriptors); + + foreach (const ZeroConfServiceEntry avahiEntry, httpServiceBrowser->serviceEntries()) { +// qCDebug(dcKodi) << "Zeroconf http entry:" << avahiEntry; + QString uuid; + foreach (const QString &txt, avahiEntry.txt()) { + if (txt.startsWith("uuid")) { + uuid = txt.split("=").last(); + break; + } + } + if (!descriptors.contains(uuid)) { + continue; + } + qCDebug(dcKodi()) << "Updating http parameter:" << avahiEntry.port(); + DeviceDescriptor descriptor = descriptors.value(uuid); + ParamList params = descriptor.params(); + params << Param(kodiDeviceHttpPortParamTypeId, avahiEntry.port()); + descriptor.setParams(params); + descriptors[uuid] = descriptor; + } + + + foreach (const DeviceDescriptor &d, descriptors.values()) { + qCDebug(dcKodi()) << "Returning descritpor:" << d.params(); + } + emit devicesDiscovered(kodiDeviceClassId, descriptors.values()); serviceBrowser->deleteLater(); + httpServiceBrowser->deleteLater(); }); return Device::DeviceErrorAsync; @@ -181,8 +232,12 @@ Device::DeviceError DevicePluginKodi::executeAction(Device *device, const Action } int commandId = -1; - if (action.actionTypeId() == kodiShowNotificationActionTypeId) { - commandId = kodi->showNotification(action.param(kodiShowNotificationActionMessageParamTypeId).value().toString(), 8000, action.param(kodiShowNotificationActionTypeParamTypeId).value().toString()); + if (action.actionTypeId() == kodiNotifyActionTypeId) { + commandId = kodi->showNotification( + action.param(kodiNotifyActionTitleParamTypeId).value().toString(), + action.param(kodiNotifyActionBodyParamTypeId).value().toString(), + 8000, + action.param(kodiNotifyActionTypeParamTypeId).value().toString()); } else if (action.actionTypeId() == kodiVolumeActionTypeId) { commandId = kodi->setVolume(action.param(kodiVolumeActionVolumeParamTypeId).value().toInt()); } else if (action.actionTypeId() == kodiMuteActionTypeId) { @@ -191,10 +246,6 @@ Device::DeviceError DevicePluginKodi::executeAction(Device *device, const Action commandId = kodi->pressButton(action.param(kodiPressButtonActionButtonParamTypeId).value().toString()); } else if (action.actionTypeId() == kodiSystemActionTypeId) { commandId = kodi->systemCommand(action.param(kodiSystemActionSystemCommandParamTypeId).value().toString()); - } else if (action.actionTypeId() == kodiVideoLibraryActionTypeId) { - commandId = kodi->videoLibrary(action.param(kodiVideoLibraryActionVideoCommandParamTypeId).value().toString()); - } else if (action.actionTypeId() == kodiAudioLibraryActionTypeId) { - commandId = kodi->audioLibrary(action.param(kodiAudioLibraryActionAudioCommandParamTypeId).value().toString()); } else if(action.actionTypeId() == kodiSkipBackActionTypeId) { commandId = kodi->pressButton("skipprevious"); } else if(action.actionTypeId() == kodiFastRewindActionTypeId) { @@ -267,6 +318,21 @@ Device::DeviceError DevicePluginKodi::executeBrowserItem(Device *device, const B return kodi->launchBrowserItem(browserAction.itemId()); } +Device::DeviceError DevicePluginKodi::executeBrowserItemAction(Device *device, const BrowserItemAction &browserItemAction) +{ + Kodi *kodi = m_kodis.key(device); + if (!kodi) { + return Device::DeviceErrorHardwareNotAvailable; + } + + int id = kodi->executeBrowserItemAction(browserItemAction.itemId(), browserItemAction.actionTypeId()); + if (id == -1) { + return Device::DeviceErrorHardwareFailure; + } + m_pendingBrowserItemActions.insert(id, browserItemAction.id()); + return Device::DeviceErrorAsync; +} + void DevicePluginKodi::onPluginTimer() { foreach (Kodi *kodi, m_kodis.keys()) { @@ -308,7 +374,15 @@ void DevicePluginKodi::onActionExecuted(int actionId, bool success) if (!m_pendingActions.contains(actionId)) { return; } - emit actionExecutionFinished(m_pendingActions.value(actionId), success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); + emit actionExecutionFinished(m_pendingActions.take(actionId), success ? Device::DeviceErrorNoError : Device::DeviceErrorInvalidParameter); +} + +void DevicePluginKodi::onBrowserItemActionExecuted(int actionId, bool success) +{ + if (!m_pendingBrowserItemActions.contains(actionId)) { + return; + } + emit browserItemActionExecutionFinished(m_pendingBrowserItemActions.take(actionId), success ? Device::DeviceErrorNoError : Device::DeviceErrorHardwareFailure); } void DevicePluginKodi::versionDataReceived(const QVariantMap &data) @@ -339,7 +413,7 @@ void DevicePluginKodi::onSetupFinished(const QVariantMap &data) emit deviceSetupFinished(device, Device::DeviceSetupStatusSuccess); - kodi->showNotification("Connected", 2000, "info"); + kodi->showNotification("nymea", tr("Connected"), 2000, "info"); } void DevicePluginKodi::onPlaybackStatusChanged(const QString &playbackStatus) diff --git a/kodi/devicepluginkodi.h b/kodi/devicepluginkodi.h index 3b6ccc64..5aa3fb50 100644 --- a/kodi/devicepluginkodi.h +++ b/kodi/devicepluginkodi.h @@ -50,7 +50,7 @@ public: Device::BrowseResult browseDevice(Device *device, Device::BrowseResult result, const QString &itemId, const QLocale &locale) override; Device::BrowserItemResult browserItem(Device *device, Device::BrowserItemResult result, const QString &itemId, const QLocale &locale) override; Device::DeviceError executeBrowserItem(Device *device, const BrowserAction &browserAction) override; -// Device::DeviceError executeBrowserItemAction(Device *device, const BrowserItemAction &browserItemAction) override; + Device::DeviceError executeBrowserItemAction(Device *device, const BrowserItemAction &browserItemAction) override; private: PluginTimer *m_pluginTimer; @@ -58,12 +58,14 @@ private: QList m_asyncSetups; QHash m_pendingActions; + QHash m_pendingBrowserItemActions; private slots: void onPluginTimer(); void onConnectionChanged(); void onStateChanged(); void onActionExecuted(int actionId, bool success); + void onBrowserItemActionExecuted(int actionId, bool success); void versionDataReceived(const QVariantMap &data); void onSetupFinished(const QVariantMap &data); diff --git a/kodi/devicepluginkodi.json b/kodi/devicepluginkodi.json index 34412b63..06ab1f25 100644 --- a/kodi/devicepluginkodi.json +++ b/kodi/devicepluginkodi.json @@ -12,7 +12,7 @@ "id": "d09953e3-c5bd-415b-973b-0d0bf2be3f69", "name": "kodi", "displayName": "Kodi", - "interfaces": ["mediaplayer", "extendedmediacontroller", "extendedvolumecontroller", "mediametadataprovider", "shufflerepeat", "connectable"], + "interfaces": ["mediaplayer", "extendedmediacontroller", "extendedvolumecontroller", "mediametadataprovider", "shufflerepeat", "notifications", "connectable"], "createMethods": ["user", "discovery"], "browsable": true, "paramTypes": [ @@ -35,6 +35,12 @@ "displayName": "HTTP port", "type" : "int", "defaultValue": 8080 + }, + { + "id": "692eb6e0-7f4e-4f43-92da-8347372287ce", + "name": "uuid", + "displayName": "UUID", + "type": "QString" } ], "stateTypes": [ @@ -198,12 +204,19 @@ }, { "id": "dc0aa3b5-4eae-4e58-a4ac-d4c124da53f1", - "name": "showNotification", + "name": "notify", "displayName": "show notification", "paramTypes": [ { "id": "798f720a-cc4f-40e7-91d7-2ef5957ca7ad", - "name": "message", + "name": "title", + "displayName": "title", + "type": "QString", + "inputType": "TextLine" + }, + { + "id": "c92d79ad-3b74-4cb6-a21b-d6a0a3cfd3e1", + "name": "body", "displayName": "message", "type": "QString", "inputType": "TextLine" @@ -286,42 +299,18 @@ ] } ] + } + ], + "browserItemActionTypes": [ + { + "id": "3fed69c5-dddf-4500-a674-c79015f63974", + "name": "updateLibrary", + "displayName": "Update library" }, { - "id": "59f7ad10-16eb-40b7-a88b-c8393ae8e413", - "name": "videoLibrary", - "displayName": "video library", - "paramTypes": [ - { - "id": "3219855b-e043-43aa-91ae-794b474379bf", - "name": "videoCommand", - "displayName": "command", - "type": "QString", - "defaultValue": "scan", - "allowedValues": [ - "scan", - "clean" - ] - } - ] - }, - { - "id": "3d5120eb-1007-46c3-b76e-d9d4b105b9f2", - "name": "audioLibrary", - "displayName": "audio library", - "paramTypes": [ - { - "id": "ba89d098-56d7-40a2-b499-c02499c1ec0c", - "name": "audioCommand", - "displayName": "command", - "type": "QString", - "defaultValue": "scan", - "allowedValues": [ - "scan", - "clean" - ] - } - ] + "id": "dbc2c455-ae75-493e-9d8b-659e951b55a1", + "name": "cleanLibrary", + "displayName": "Clean library" } ] } diff --git a/kodi/kodi.cpp b/kodi/kodi.cpp index 13ac75fe..ac5d2ad4 100644 --- a/kodi/kodi.cpp +++ b/kodi/kodi.cpp @@ -24,6 +24,7 @@ #include #include "extern-plugininfo.h" #include +#include Kodi::Kodi(const QHostAddress &hostAddress, int port, int httpPort, QObject *parent) : QObject(parent), @@ -49,44 +50,11 @@ Kodi::Kodi(const QHostAddress &hostAddress, int port, int httpPort, QObject *par QVariantList properties; properties.append("thumbnail"); - // Music - BrowserItem item = BrowserItem("audiolibrary", tr("Music library"), true); - item.setDescription(tr("")); - item.setIcon(BrowserItem::BrowserIconFolder); - VirtualFsNode *audioLibrary = new VirtualFsNode(item); - m_virtualFs->addChild(audioLibrary); - - item = BrowserItem("artists", tr("Artists"), true); - item.setDescription(tr("")); - item.setIcon(BrowserItem::BrowserIconFolder); - VirtualFsNode *artists = new VirtualFsNode(item); - artists->getMethod = "AudioLibrary.GetArtists"; - artists->getParams.insert("sort", sort); - artists->getParams.insert("properties", properties); - audioLibrary->addChild(artists); - - item = BrowserItem("albums", tr("Albums"), true); - item.setDescription(tr("")); - item.setIcon(BrowserItem::BrowserIconFolder); - VirtualFsNode *albums = new VirtualFsNode(item); - albums->getMethod = "AudioLibrary.GetAlbums"; - albums->getParams.insert("sort", sort); - albums->getParams.insert("properties", properties); - audioLibrary->addChild(albums); - - item = BrowserItem("songs", tr("Songs"), true); - item.setDescription(tr("")); - item.setIcon(BrowserItem::BrowserIconFolder); - VirtualFsNode *songs = new VirtualFsNode(item); - songs->getMethod = "AudioLibrary.GetSongs"; - songs->getParams.insert("sort", sort); - songs->getParams.insert("properties", properties); - audioLibrary->addChild(songs); - // Video - item = BrowserItem("videolibrary", tr("Video library"), true); + BrowserItem item = BrowserItem("videolibrary", tr("Video library"), true); item.setDescription(tr("")); item.setIcon(BrowserItem::BrowserIconFolder); + item.setActionTypeIds({kodiUpdateLibraryBrowserItemActionTypeId, kodiCleanLibraryBrowserItemActionTypeId}); VirtualFsNode *videoLibrary = new VirtualFsNode(item); m_virtualFs->addChild(videoLibrary); @@ -96,7 +64,11 @@ Kodi::Kodi(const QHostAddress &hostAddress, int port, int httpPort, QObject *par VirtualFsNode *movies = new VirtualFsNode(item); movies->getMethod = "VideoLibrary.GetMovies"; movies->getParams.insert("sort", sort); - movies->getParams.insert("properties", properties); + QVariantList movieProperties = properties; + movieProperties.append("year"); + movieProperties.append("rating"); + movieProperties.append("runtime"); + movies->getParams.insert("properties", movieProperties); videoLibrary->addChild(movies); item = BrowserItem("tvshows", tr("TV Shows"), true); @@ -105,7 +77,11 @@ Kodi::Kodi(const QHostAddress &hostAddress, int port, int httpPort, QObject *par VirtualFsNode *tvShows = new VirtualFsNode(item); tvShows->getMethod = "VideoLibrary.GetTVShows"; tvShows->getParams.insert("sort", sort); - tvShows->getParams.insert("properties", properties); + QVariantList tvShowProperties = properties; + tvShowProperties.append("year"); + tvShowProperties.append("rating"); + tvShowProperties.append("season"); + tvShows->getParams.insert("properties", tvShowProperties); videoLibrary->addChild(tvShows); item = BrowserItem("musicvideos", tr("Music Videos"), true); @@ -117,6 +93,52 @@ Kodi::Kodi(const QHostAddress &hostAddress, int port, int httpPort, QObject *par musicVideos->getParams.insert("properties", properties); videoLibrary->addChild(musicVideos); + // Music + item = BrowserItem("audiolibrary", tr("Music library"), true); + item.setDescription(tr("")); + item.setIcon(BrowserItem::BrowserIconFolder); + item.setActionTypeIds({kodiUpdateLibraryBrowserItemActionTypeId, kodiCleanLibraryBrowserItemActionTypeId}); + VirtualFsNode *audioLibrary = new VirtualFsNode(item); + m_virtualFs->addChild(audioLibrary); + + item = BrowserItem("artists", tr("Artists"), true); + item.setDescription(tr("")); + item.setIcon(BrowserItem::BrowserIconFolder); + VirtualFsNode *artists = new VirtualFsNode(item); + artists->getMethod = "AudioLibrary.GetArtists"; + artists->getParams.insert("sort", sort); + QVariantList artistProperties = properties; + artistProperties.append("formed"); + artistProperties.append("genre"); + artists->getParams.insert("properties", artistProperties); + audioLibrary->addChild(artists); + + item = BrowserItem("albums", tr("Albums"), true); + item.setDescription(tr("")); + item.setIcon(BrowserItem::BrowserIconFolder); + VirtualFsNode *albums = new VirtualFsNode(item); + albums->getMethod = "AudioLibrary.GetAlbums"; + albums->getParams.insert("sort", sort); + QVariantList albumProperties = properties; + albumProperties.append("artist"); + albumProperties.append("year"); + albums->getParams.insert("properties", albumProperties); + audioLibrary->addChild(albums); + + item = BrowserItem("songs", tr("Songs"), true); + item.setDescription(tr("")); + item.setIcon(BrowserItem::BrowserIconFolder); + VirtualFsNode *songs = new VirtualFsNode(item); + songs->getMethod = "AudioLibrary.GetSongs"; + songs->getParams.insert("sort", sort); + QVariantList songProperties = properties; + songProperties.append("artist"); + songProperties.append("album"); + songProperties.append("year"); + songs->getParams.insert("properties", songProperties); + audioLibrary->addChild(songs); + + // Add-ons item = BrowserItem("addons", tr("Add-ons"), true); item.setDescription(tr("")); item.setIcon(BrowserItem::BrowserIconFolder); @@ -203,10 +225,10 @@ int Kodi::setRepeat(const QString &repeat) return m_jsonHandler->sendData("Player.SetRepeat", params); } -int Kodi::showNotification(const QString &message, const int &displayTime, const QString ¬ificationType) +int Kodi::showNotification(const QString &title, const QString &message, const int &displayTime, const QString ¬ificationType) { QVariantMap params; - params.insert("title", "nymea notification"); + params.insert("title", title); params.insert("message", message); params.insert("displaytime", displayTime); params.insert("image", notificationType); @@ -239,34 +261,6 @@ int Kodi::systemCommand(const QString &command) return m_jsonHandler->sendData("System." + method, QVariantMap()); } -int Kodi::videoLibrary(const QString &command) -{ - QString method; - if (command == "scan") { - method = "Scan"; - } else if (command == "clean") { - method = "Clean"; - } else { - // already checkt with allowed values - } - - return m_jsonHandler->sendData("VideoLibrary." + method, QVariantMap()); -} - -int Kodi::audioLibrary(const QString &command) -{ - QString method; - if (command == "scan") { - method = "Scan"; - } else if (command == "clean") { - method = "Clean"; - } else { - // already checkt with allowed values - } - - return m_jsonHandler->sendData("AudioLibrary." + method, QVariantMap()); -} - void Kodi::update() { QVariantMap params; @@ -331,7 +325,10 @@ Device::BrowseResult Kodi::browse(const QString &itemId, Device::BrowseResult &r filter.insert("artistid", idString.toInt()); QVariantMap params; params.insert("filter", filter); - params.insert("properties", properties); + QVariantList albumProperties = properties; + albumProperties.append("artist"); + albumProperties.append("year"); + params.insert("properties", albumProperties); int id = m_jsonHandler->sendData("AudioLibrary.GetAlbums", params); m_pendingBrowseRequests.insert(id, result); result.status = Device::DeviceErrorAsync; @@ -345,10 +342,12 @@ Device::BrowseResult Kodi::browse(const QString &itemId, Device::BrowseResult &r filter.insert("albumid", idString.toInt()); QVariantMap params; params.insert("filter", filter); - QVariantList properties; - properties.append("thumbnail"); - properties.append("albumid"); - params.insert("properties", properties); + QVariantList songProperties = properties; + songProperties.append("albumid"); + songProperties.append("artist"); + songProperties.append("album"); + songProperties.append("year"); + params.insert("properties", songProperties); int id = m_jsonHandler->sendData("AudioLibrary.GetSongs", params); m_pendingBrowseRequests.insert(id, result); result.status = Device::DeviceErrorAsync; @@ -364,6 +363,7 @@ Device::BrowseResult Kodi::browse(const QString &itemId, Device::BrowseResult &r properties.append("tvshowid"); properties.append("season"); properties.append("thumbnail"); + properties.append("showtitle"); params.insert("properties", properties); int id = m_jsonHandler->sendData("VideoLibrary.GetSeasons", params); m_pendingBrowseRequests.insert(id, result); @@ -380,6 +380,10 @@ Device::BrowseResult Kodi::browse(const QString &itemId, Device::BrowseResult &r QVariantMap params; params.insert("tvshowid", tvShowId); params.insert("season", seasonId); + QVariantList properties; + properties.append("thumbnail"); + properties.append("showtitle"); + properties.append("season"); params.insert("properties", properties); qCDebug(dcKodi()) << "getting episodes:" << params; int id = m_jsonHandler->sendData("VideoLibrary.GetEpisodes", params); @@ -505,6 +509,29 @@ Device::DeviceError Kodi::launchBrowserItem(const QString &itemId) return Device::DeviceErrorNoError; } +int Kodi::executeBrowserItemAction(const QString &itemId, const ActionTypeId &actionTypeId) +{ + QString scope; + QString method; + if (actionTypeId == kodiUpdateLibraryBrowserItemActionTypeId) { + method = "Scan"; + } else if (actionTypeId == kodiCleanLibraryBrowserItemActionTypeId) { + method = "Clean"; + } else { + return -1; + } + + if (itemId == "audiolibrary") { + scope = "AudioLibrary"; + } else if (itemId == "videolibrary") { + scope = "VideoLibrary"; + } else { + return -1; + } + + return m_jsonHandler->sendData(scope + "." + method, QVariantMap()); +} + void Kodi::onVolumeChanged(const int &volume, const bool &muted) { if (m_volume != volume || m_muted != muted) { @@ -623,10 +650,7 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res 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") { @@ -661,7 +685,7 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res if (method == "Player.SetShuffle" || method == "Player.SetRepeat") { updatePlayerProperties(); - emit actionExecuted(id, true); + emit actionExecuted(id, !response.contains("error")); return; } @@ -674,6 +698,14 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res item.setBrowsable(true); item.setIcon(BrowserItem::BrowserIconFolder); item.setThumbnail(prepareThumbnail(artist.value("thumbnail").toString())); + QStringList description; + if (!artist.value("formed").toString().isEmpty()) { + description.append(artist.value("formed").toString()); + } + if (!artist.value("genre").toStringList().isEmpty()) { + description.append(artist.value("genre").toStringList().join(", ")); + } + item.setDescription(description.join(" - ")); qCDebug(dcKodi()) << "Thumbnail" << item.thumbnail(); result.items.append(item); } @@ -689,6 +721,14 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res item.setBrowsable(true); item.setIcon(BrowserItem::BrowserIconFolder); item.setThumbnail(prepareThumbnail(album.value("thumbnail").toString())); + QStringList description; + if (!album.value("artist").toStringList().isEmpty()) { + description.append(album.value("artist").toStringList().join(", ")); + } + if (album.value("year").toInt() != 0) { + description.append(album.value("year").toString()); + } + item.setDescription(description.join(" - ")); result.items.append(item); } emit browseResult(result); @@ -712,6 +752,16 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res item.setExecutable(true); item.setIcon(BrowserItem::BrowserIconMusic); item.setThumbnail(prepareThumbnail(song.value("thumbnail").toString())); + QStringList description; + if (!song.value("artist").toStringList().isEmpty()) { + description.append(song.value("artist").toStringList().join(",")); + } + if (!song.value("album").toString().isEmpty()) { + description.append(song.value("album").toString()); + } else if (!song.value("year").toString().isEmpty()) { + description.append(song.value("year").toString()); + } + item.setDescription(description.join(" - ")); result.items.append(item); i++; } @@ -729,6 +779,21 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res item.setExecutable(true); item.setIcon(BrowserItem::BrowserIconVideo); item.setThumbnail(prepareThumbnail(movie.value("thumbnail").toString())); + QString rating; + for (int i = 0; i < 5; i++) { + if (qRound(movie.value("rating").toDouble() / 2) >= i) { + rating += "★"; + } else { + rating += "☆"; + } + } + + int runtime = movie.value("runtime").toInt(); + int hours = runtime / 60 / 60; + int minutes = (runtime / 60) % 60; + QString duration; + duration = QString("%1:%2").arg(hours).arg(minutes, 2, 10, QChar('0')); + item.setDescription(movie.value("year").toString() + " - " + duration + " - " + rating); result.items.append(item); } emit browseResult(result); @@ -744,6 +809,15 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res item.setBrowsable(true); item.setIcon(BrowserItem::BrowserIconFolder); item.setThumbnail(prepareThumbnail(tvShow.value("thumbnail").toString())); + QString rating; + for (int i = 0; i < 5; i++) { + if (qRound(tvShow.value("rating").toDouble() / 2) >= i) { + rating += "★"; + } else { + rating += "☆"; + } + } + item.setDescription(tvShow.value("year").toString() + " - " + tr("%1 seasons").arg(tvShow.value("season").toInt()) + " - " + rating); result.items.append(item); } emit browseResult(result); @@ -759,6 +833,7 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res item.setBrowsable(true); item.setIcon(BrowserItem::BrowserIconFolder); item.setThumbnail(prepareThumbnail(season.value("thumbnail").toString())); + item.setDescription(season.value("showtitle").toString()); result.items.append(item); } emit browseResult(result); @@ -774,6 +849,11 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res item.setExecutable(true); item.setIcon(BrowserItem::BrowserIconVideo); item.setThumbnail(prepareThumbnail(episode.value("thumbnail").toString())); + if (!episode.value("season").toString().isEmpty()) { + item.setDescription(episode.value("showtitle").toString() + " - " + tr("Season %1").arg(episode.value("season").toString())); + } else { + item.setDescription(episode.value("showtitle").toString()); + } result.items.append(item); } emit browseResult(result); @@ -803,6 +883,7 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res BrowserItem item("addon:" + addon.value("addonid").toString(), addon.value("name").toString()); item.setBrowsable(true); item.setIcon(BrowserItem::BrowserIconApplication); + item.setThumbnail(prepareThumbnail(addon.value("thumbnail").toString())); result.items.append(item); } emit browseResult(result); @@ -825,6 +906,7 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res item.setExecutable(true); item.setIcon(BrowserItem::BrowserIconMusic); } + item.setThumbnail(prepareThumbnail(file.value("thumbnail").toString())); result.items.append(item); } emit browseResult(result); @@ -867,6 +949,16 @@ void Kodi::processResponse(int id, const QString &method, const QVariantMap &res return; } + if (method == "GUI.ShowNotification") { + emit actionExecuted(id, !response.contains("error")); + return; + } + + if (method == "VideoLibrary.Scan" || method == "VideoLibrary.Clean" || method == "AudioLibrary.Scan" || method == "AudioLibrary.Clean") { + emit browserItemActionExecuted(id, !response.contains("error")); + return; + } + qCWarning(dcKodi()) << "unhandled reply" << method << response; } @@ -896,8 +988,12 @@ QString Kodi::prepareThumbnail(const QString &thumbnail) return QString(); } + QString addr = m_connection->hostAddress().toString(); + if (m_connection->hostAddress().protocol() == QAbstractSocket::IPv6Protocol) { + addr = '[' + addr + ']'; + } return QString("http://%1:%2/image/%3") - .arg(m_connection->hostAddress().toString()) + .arg(addr) .arg(m_httpPort) .arg(QString(thumbnail.toUtf8().toPercentEncoding())); } diff --git a/kodi/kodi.h b/kodi/kodi.h index 91ea9d2a..830dcb2e 100644 --- a/kodi/kodi.h +++ b/kodi/kodi.h @@ -30,6 +30,7 @@ #include "kodijsonhandler.h" #include "types/browseritem.h" +#include "types/browseritemaction.h" #include "devices/device.h" class Kodi : public QObject @@ -55,11 +56,9 @@ public: int setRepeat(const QString &repeat); // actions - int showNotification(const QString &message, const int &displayTime, const QString ¬ificationType); + int showNotification(const QString &title, const QString &message, const int &displayTime, const QString ¬ificationType); int pressButton(const QString &button); int systemCommand(const QString &command); - int videoLibrary(const QString &command); - int audioLibrary(const QString &command); void update(); void checkVersion(); @@ -70,12 +69,13 @@ public: Device::BrowseResult browse(const QString &itemId, Device::BrowseResult &result); Device::BrowserItemResult browserItem(const QString &itemId, Device::BrowserItemResult &result); Device::DeviceError launchBrowserItem(const QString &itemId); + int executeBrowserItemAction(const QString &itemId, const ActionTypeId &actionTypeId); signals: void connectionStatusChanged(); void stateChanged(); void activePlayerChanged(const QString &playerType); - void actionExecuted(int actionId, const bool &success); + void actionExecuted(int actionId, bool success); void updateDataReceived(const QVariantMap &data); void versionDataReceived(const QVariantMap &data); void playbackStatusChanged(const QString &playbackState); @@ -84,6 +84,7 @@ signals: void repeatChanged(const QString &repeat); void browseResult(const Device::BrowseResult &result); void browserItemResult(const Device::BrowserItemResult &result); + void browserItemActionExecuted(int actionId, bool success); private slots: void onVolumeChanged(const int &volume, const bool &muted);