From 5d9b6c55e15e933f422c67576748dde4fd4c04d3 Mon Sep 17 00:00:00 2001 From: "bernhard.trinnes" Date: Tue, 17 Mar 2020 11:44:50 +0100 Subject: [PATCH] added first level browsing --- bluos/bluos.cpp | 127 ++++++++++++++++++++++++++++--- bluos/bluos.h | 2 + bluos/integrationpluginbluos.cpp | 106 +++++++++++++++++++++++--- bluos/integrationpluginbluos.h | 2 +- 4 files changed, 215 insertions(+), 22 deletions(-) diff --git a/bluos/bluos.cpp b/bluos/bluos.cpp index b6251496..64875f27 100644 --- a/bluos/bluos.cpp +++ b/bluos/bluos.cpp @@ -78,7 +78,7 @@ QUuid BluOS::getStatus() } emit connectionChanged(true); QByteArray data = reply->readAll(); - qCDebug(dcBluOS()) << "Get Status:" << data; + //qCDebug(dcBluOS()) << "Get Status:" << data; parseState(data); }); return requestId; @@ -283,8 +283,9 @@ QUuid BluOS::listPresets() } emit connectionChanged(true); + QByteArray data = reply->readAll(); QXmlStreamReader xml; - xml.addData(reply->readAll()); + xml.addData(data); if (xml.hasError()) { qCDebug(dcBluOS()) << "XML Error:" << xml.errorString(); return; @@ -298,12 +299,13 @@ QUuid BluOS::listPresets() if (xml.attributes().hasAttribute("id")) { preset.Id = xml.attributes().value("id").toInt(); } - if (xml.attributes().hasAttribute("name")) { - preset.Name = xml.attributes().value("name").toString(); - } - if (xml.attributes().hasAttribute("url")) { + if (xml.attributes().hasAttribute("name")) { + preset.Name = xml.attributes().value("name").toString(); + } + if (xml.attributes().hasAttribute("url")) { preset.Url = xml.attributes().value("url").toString(); - } + } + qCDebug(dcBluOS()) << "Preset text" << xml.readElementText(); //apparently the text must be read so the xml parser recognises the next element presetList.append(preset); } else { xml.skipCurrentElement(); @@ -325,12 +327,13 @@ QUuid BluOS::loadPreset(int preset) url.setScheme("http"); url.setHost(m_hostAddress.toString()); url.setPort(m_port); - url.setPath("/Presets"); + url.setPath("/Preset"); QUrlQuery query; query.addQueryItem("id", QString::number(preset)); url.setQuery(query); + qCDebug(dcBluOS()) << "Loading preset" << url.toString(); QNetworkReply *reply = m_networkManager->get(QNetworkRequest(url)); - connect(reply, &QNetworkReply::finished, this, [reply, this] { + connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] { reply->deleteLater(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -339,11 +342,12 @@ QUuid BluOS::loadPreset(int preset) if (reply->error() == QNetworkReply::HostNotFoundError) { emit connectionChanged(false); } - + emit actionExecuted(requestId, false); qCWarning(dcBluOS()) << "Request error:" << status << reply->errorString(); return; } emit connectionChanged(true); + emit actionExecuted(requestId, true); }); return requestId; } @@ -358,7 +362,7 @@ QUuid BluOS::getSources() url.setPort(m_port); url.setPath("/Browse"); QNetworkReply *reply = m_networkManager->get(QNetworkRequest(url)); - connect(reply, &QNetworkReply::finished, this, [reply, this] { + connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] { reply->deleteLater(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -367,11 +371,110 @@ QUuid BluOS::getSources() if (reply->error() == QNetworkReply::HostNotFoundError) { emit connectionChanged(false); } - qCWarning(dcBluOS()) << "Request error:" << status << reply->errorString(); return; } emit connectionChanged(true); + QByteArray data = reply->readAll(); + qCDebug(dcBluOS()) << "Sources: " << data; + QXmlStreamReader xml; + xml.addData(data); + if (xml.hasError()) { + qCDebug(dcBluOS()) << "XML Error:" << xml.errorString(); + return; + } + QList sourceList; + if (xml.readNextStartElement()) { + if (xml.name() == "browse") { + while(xml.readNextStartElement()){ + if(xml.name() == "item"){ + Source source; + if (xml.attributes().hasAttribute("text")) { + source.Text = xml.attributes().value("text").toString(); + } + if (xml.attributes().hasAttribute("type")) { + source.Type = xml.attributes().value("type").toString(); + } + if (xml.attributes().hasAttribute("browseKey")) { + source.BrowseKey = xml.attributes().value("browseKey").toString(); + } + if (xml.attributes().hasAttribute("image")) { + source.Image = xml.attributes().value("image").toString(); + } + qCDebug(dcBluOS()) << "Source text" << xml.readElementText(); + sourceList.append(source); + } else { + xml.skipCurrentElement(); + } + } + } + } + emit sourcesReceived(requestId, sourceList); + }); + return requestId; +} + +QUuid BluOS::browseSource(const QString &key) +{ + QUuid requestId = QUuid::createUuid(); + + QUrl url; + url.setScheme("http"); + url.setHost(m_hostAddress.toString()); + url.setPort(m_port); + url.setPath("/Browse"); + QUrlQuery query; + query.addQueryItem("key", key); + url.setQuery(query); + QNetworkReply *reply = m_networkManager->get(QNetworkRequest(url)); + connect(reply, &QNetworkReply::finished, this, [requestId, reply, this] { + reply->deleteLater(); + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + // Check HTTP status code + if (status != 200 || reply->error() != QNetworkReply::NoError) { + if (reply->error() == QNetworkReply::HostNotFoundError) { + emit connectionChanged(false); + } + qCWarning(dcBluOS()) << "Request error:" << status << reply->errorString(); + return; + } + emit connectionChanged(true); + QByteArray data = reply->readAll(); + qCDebug(dcBluOS()) << "Browse result: " << data; + QXmlStreamReader xml; + xml.addData(data); + if (xml.hasError()) { + qCDebug(dcBluOS()) << "XML Error:" << xml.errorString(); + return; + } + QList sourceList; + if (xml.readNextStartElement()) { + if (xml.name() == "browse") { + while(xml.readNextStartElement()){ + if(xml.name() == "item"){ + Source source; + if (xml.attributes().hasAttribute("text")) { + source.Text = xml.attributes().value("text").toString(); + } + if (xml.attributes().hasAttribute("type")) { + source.Type = xml.attributes().value("type").toString(); + } + if (xml.attributes().hasAttribute("browseKey")) { + source.BrowseKey = xml.attributes().value("browseKey").toString(); + } + if (xml.attributes().hasAttribute("image")) { + source.Image = xml.attributes().value("image").toString(); + } + qCDebug(dcBluOS()) << "Source text" << xml.readElementText(); + sourceList.append(source); + } else { + xml.skipCurrentElement(); + } + } + } + } + emit sourcesReceived(requestId, sourceList); }); return requestId; } diff --git a/bluos/bluos.h b/bluos/bluos.h index 0dacabcd..f929a0ab 100644 --- a/bluos/bluos.h +++ b/bluos/bluos.h @@ -122,6 +122,7 @@ public: // Content Browsing QUuid getSources(); + QUuid browseSource(const QString &key); // Player Grouping QUuid addGroupPlayer(QHostAddress address, int port); //adds player as slave @@ -144,5 +145,6 @@ signals: void presetsReceived(QUuid requestId, const QList &presets); void sourcesReceived(QUuid requestId, const QList &sources); + void browseResultReceived(QUuid requestId, const QList &sources); }; #endif // BLUOS_H diff --git a/bluos/integrationpluginbluos.cpp b/bluos/integrationpluginbluos.cpp index 6b229152..1d386136 100644 --- a/bluos/integrationpluginbluos.cpp +++ b/bluos/integrationpluginbluos.cpp @@ -232,20 +232,28 @@ void IntegrationPluginBluOS::browseThing(BrowseResult *result) QUuid requestId = bluos->listPresets(); m_asyncBrowseResults.insert(requestId, result); connect(result, &BrowseResult::aborted, this, [this, requestId]{m_asyncBrowseResults.remove(requestId);}); - } else { + } else if (result->itemId() == "grouping") { + //TODO avahi discovery + // m_asyncBrowseResults.insert(requestId, result); + //connect(result, &BrowseResult::aborted, this, [this, requestId]{m_asyncBrowseResults.remove(requestId);}); + } else if (result->itemId().isEmpty()) { MediaBrowserItem presetItem("presets", "Presets", true, false); presetItem.setIcon(BrowserItem::BrowserIcon::BrowserIconFavorites); presetItem.setMediaIcon(MediaBrowserItem::MediaBrowserIconMusicLibrary); result->addItem(presetItem); MediaBrowserItem groupingItem("grouping", "Grouping", true, false); - presetItem.setIcon(BrowserItem::BrowserIcon::BrowserIconApplication); - presetItem.setMediaIcon(MediaBrowserItem::MediaBrowserIconNetwork); - result->addItem(presetItem); + groupingItem.setIcon(BrowserItem::BrowserIcon::BrowserIconApplication); + groupingItem.setMediaIcon(MediaBrowserItem::MediaBrowserIconNetwork); + result->addItem(groupingItem); QUuid requestId = bluos->getSources(); m_asyncBrowseResults.insert(requestId, result); connect(result, &BrowseResult::aborted, this, [this, requestId]{m_asyncBrowseResults.remove(requestId);}); + } else { + QUuid requestId = bluos->browseSource(result->itemId()); + m_asyncBrowseResults.insert(requestId, result); + connect(result, &BrowseResult::aborted, this, [this, requestId]{m_asyncBrowseResults.remove(requestId);}); } } } @@ -274,7 +282,24 @@ void IntegrationPluginBluOS::browserItem(BrowserItemResult *result) void IntegrationPluginBluOS::executeBrowserItem(BrowserActionInfo *info) { - Q_UNUSED(info) + Thing *thing = info->thing(); + if (thing->thingClassId() == bluosPlayerThingClassId) { + BluOS *bluos = m_bluos.value(thing->id()); + if (!bluos) + return; + + if (info->browserAction().itemId().startsWith("presets")) { + QUuid requestId; + int presetId = info->browserAction().itemId().split("&").last().toInt(); + requestId = bluos->loadPreset(presetId); + m_asyncExecuteBrowseItems.insert(requestId, info); + connect(info, &BrowserActionInfo::aborted, this, [this, requestId]{m_asyncExecuteBrowseItems.remove(requestId);}); + } else if (info->browserAction().itemId().startsWith("grouping")) { + //TODO Grouping + } else { + //TODO Sources + } + } } void IntegrationPluginBluOS::onConnectionChanged(bool connected) @@ -351,6 +376,15 @@ void IntegrationPluginBluOS::onActionExecuted(QUuid requestId, bool success) info->finish(Thing::ThingErrorHardwareNotAvailable); } } + if (m_asyncExecuteBrowseItems.contains(requestId)) { + BrowserActionInfo *info = m_asyncExecuteBrowseItems.take(requestId); + if (success) { + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorHardwareFailure); + } + m_pluginTimer->timeout(); // get a status update + } } void IntegrationPluginBluOS::onVolumeReceived(int volume, bool mute) @@ -369,10 +403,10 @@ void IntegrationPluginBluOS::onPresetsReceived(QUuid requestId, const QListaddItem(item); @@ -394,9 +428,63 @@ void IntegrationPluginBluOS::onSourcesReceived(QUuid requestId, const QListaddItem(item); + } + result->finish(Thing::ThingErrorNoError); + } + if (m_asyncBrowseItemResults.contains(requestId)) { + BrowserItemResult *result = m_asyncBrowseItemResults.take(requestId); + Q_UNUSED(result) + } +} + +void IntegrationPluginBluOS::onBrowseResultReceived(QUuid requestId, const QList &sources) +{ + BluOS *bluos = static_cast(sender()); + Thing *thing = myThings().findById(m_bluos.key(bluos)); + if (!thing) + return; + if (m_asyncBrowseResults.contains(requestId)) { + BrowseResult *result = m_asyncBrowseResults.take(requestId); + foreach(BluOS::Source source, sources) { + qCDebug(dcBluOS()) << "Source added" << source.Text << source.BrowseKey << source.Type; + MediaBrowserItem item; + item.setDisplayName(source.Text); + if (source.BrowseKey.isEmpty()) { + item.setBrowsable(false); + item.setExecutable(true); + item.setId(source.Text); + } else { + item.setBrowsable(true); + item.setExecutable(false); + item.setId(source.BrowseKey); + } + item.setIcon(BrowserItem::BrowserIconMusic); result->addItem(item); } result->finish(Thing::ThingErrorNoError); diff --git a/bluos/integrationpluginbluos.h b/bluos/integrationpluginbluos.h index b027d7d1..b61dce7c 100644 --- a/bluos/integrationpluginbluos.h +++ b/bluos/integrationpluginbluos.h @@ -86,6 +86,6 @@ private slots: void onPresetsReceived(QUuid requestId, const QList &presets); void onSourcesReceived(QUuid requestId, const QList &sources); + void onBrowseResultReceived(QUuid requestId, const QList &sources); }; - #endif // INTEGRATIONPLUGINBLUOS_H