improved browsing on TuneIn

master
nymea 2019-10-08 00:38:48 +02:00 committed by bernhard.trinnes
parent 127142af66
commit 811349c444
5 changed files with 199 additions and 89 deletions

View File

@ -404,7 +404,7 @@ void Heos::browseSourceContainers(const QString &sourceId, const QString &contai
queryParams.addQueryItem("cid", containerId);
cmd.append(queryParams.toString());
cmd.append("\r\n");
qCDebug(dcDenon) << "playing station:" << cmd;
qCDebug(dcDenon) << "Browsing container:" << cmd;
m_socket->write(cmd);
}
@ -459,7 +459,19 @@ void Heos::playUrl(int playerId, const QUrl &mediaUrl)
m_socket->write(cmd);
}
void Heos::addContainerToQueue(int playerId, const QString &sourceId, const QString &containerId, ADD_CRITERIA addCriteria)
{
QByteArray cmd("heos://browse/add_to_queue?");
QUrlQuery queryParams;
queryParams.addQueryItem("pid", QString::number(playerId));
queryParams.addQueryItem("sid", sourceId);
queryParams.addQueryItem("cid", containerId);
queryParams.addQueryItem("aid", QString::number(addCriteria));
cmd.append(queryParams.toString());
cmd.append("\r\n");
qCDebug(dcDenon) << "Adding to queue:" << cmd;
m_socket->write(cmd);
}
void Heos::onConnected()
{
@ -498,9 +510,10 @@ void Heos::readData()
if (dataMap.contains("heos")) {
QString command = dataMap.value("heos").toMap().value("command").toString();
QUrlQuery message(dataMap.value("heos").toMap().value("message").toString());
bool success = false;
if(dataMap.value("heos").toMap().contains("result")) {
//If the message doesn't contain result it is an event message
bool success = dataMap.value("heos").toMap().value("result").toString().contains("success");
success = dataMap.value("heos").toMap().value("result").toString().contains("success");
if (!success) {
qDebug(dcDenon()) << "Command:" << command << "was not successfull. Message:" << message.toString();
}
@ -540,7 +553,8 @@ void Heos::readData()
}
}
/* 4.2 Player Commands 4.2.1 Get Players
/* 4.2 Player Commands
* 4.2.1 Get Players
* 4.2.2 Get Player Info
* 4.2.3 Get Play State
* 4.2.4 Set Play State
@ -734,28 +748,8 @@ void Heos::readData()
qDebug(dcDenon()) << "Get music source request response received" << command;
QVariantList payloadVariantList = jsonDoc.toVariant().toMap().value("payload").toList();
QList<MusicSourceObject> musicSources;
foreach (const QVariant &payloadEntryVariant, payloadVariantList) {
MusicSourceObject source;
source.name = payloadEntryVariant.toMap().value("name").toString();
source.image_url = payloadEntryVariant.toMap().value("image_url").toString();
source.type = payloadEntryVariant.toMap().value("type").toString();
source.sourceId = payloadEntryVariant.toMap().value("sid").toInt();
source.available = payloadEntryVariant.toMap().value("available").toString().contains("true");
source.serviceUsername = payloadEntryVariant.toMap().value("service_username").toString();
musicSources.append(source);
}
emit musicSourcesReceived(musicSources);
} else if (command.contains("browse/browse")) {
QVariantList payloadVariantList = jsonDoc.toVariant().toMap().value("payload").toList();
QList<MusicSourceObject> musicSources;
QList<MediaObject> mediaItems;
foreach (const QVariant &payloadEntryVariant, payloadVariantList) {
QString type = payloadEntryVariant.toMap().value("type").toString();
if (type == "source") {
if (success) {
foreach (const QVariant &payloadEntryVariant, payloadVariantList) {
MusicSourceObject source;
source.name = payloadEntryVariant.toMap().value("name").toString();
source.image_url = payloadEntryVariant.toMap().value("image_url").toString();
@ -764,14 +758,65 @@ void Heos::readData()
source.available = payloadEntryVariant.toMap().value("available").toString().contains("true");
source.serviceUsername = payloadEntryVariant.toMap().value("service_username").toString();
musicSources.append(source);
} else if (type == "container" || type == "album" || type == "song") {
MediaObject media;
media.name = payloadEntryVariant.toMap().value("name").toString();
media.imageUrl = payloadEntryVariant.toMap().value("image_url").toString();
mediaItems.append(media);
}
emit browseRequestReceived(musicSources, mediaItems);
emit musicSourcesReceived(musicSources);
}
} else if (command.contains("browse/browse")) {
QVariantList payloadVariantList = jsonDoc.toVariant().toMap().value("payload").toList();
QString sourceId = message.queryItemValue("sid");
QString containerId = message.queryItemValue("cid");
if (message.toString().contains("command under process")){
qDebug(dcDenon()) << "Browse command is beeing processed";
return;
}
if (success) {
QList<MusicSourceObject> musicSources;
QList<MediaObject> mediaItems;
foreach (const QVariant &payloadEntryVariant, payloadVariantList) {
QString type = payloadEntryVariant.toMap().value("type").toString();
if (type == "source") {
MusicSourceObject source;
source.name = payloadEntryVariant.toMap().value("name").toString();
source.image_url = payloadEntryVariant.toMap().value("image_url").toString();
source.type = payloadEntryVariant.toMap().value("type").toString();
source.sourceId = payloadEntryVariant.toMap().value("sid").toInt();
qDebug(dcDenon()) << "Source" << source.name << source.type << source.sourceId << payloadEntryVariant.toMap().value("sid");
//source.available = payloadEntryVariant.toMap().value("available").toString().contains("true");
//source.serviceUsername = payloadEntryVariant.toMap().value("service_username").toString();
musicSources.append(source);
} else {
MediaObject media;
qDebug(dcDenon()) << "Media Item" << payloadEntryVariant.toMap().value("mid").toString() << payloadEntryVariant.toMap().value("cid").toString();
media.name = payloadEntryVariant.toMap().value("name").toString();
media.containerId = payloadEntryVariant.toMap().value("cid").toString();
media.mediaId = payloadEntryVariant.toMap().value("mid").toString();
media.imageUrl = payloadEntryVariant.toMap().value("image_url").toString();
media.isPlayable = payloadEntryVariant.toMap().value("playable").toString().contains("yes");
media.isContainer = payloadEntryVariant.toMap().value("container").toString().contains("yes");
if (type == "artist") {
media.mediaType = MEDIA_TYPE_ARTIST;
} else if (type == "song") {
media.mediaType = MEDIA_TYPE_SONG;
} else if (type == "genre") {
media.mediaType = MEDIA_TYPE_GENRE;
} else if (type == "station") {
media.mediaType = MEDIA_TYPE_STATION;
} else if (type == "album") {
media.mediaType = MEDIA_TYPE_ALBUM;
} else if (type == "container") {
media.mediaType = MEDIA_TYPE_CONTAINER;
}
mediaItems.append(media);
}
}
emit browseRequestReceived(sourceId, containerId, musicSources, mediaItems);
}
else {
int errorId = message.queryItemValue("eid").toInt();
QString text = message.queryItemValue("text");
emit browseErrorReceived(sourceId, containerId, errorId, text);
}
} else if (command.contains("play_preset")) {

View File

@ -108,6 +108,7 @@ public:
void playPresetStation(int playerId, int presetNumber);
void playInputSource(int playerId, const QString &inputName); //Validity of Inputs depends on the type of source HEOS devic
void playUrl(int playerId, const QUrl &url);
void addContainerToQueue(int playerId, const QString &sourceId, const QString &containerId, ADD_CRITERIA addCriteria);
private:
bool m_eventRegistered = false;
@ -139,10 +140,10 @@ signals:
void sourcesChanged();
void nowPlayingMediaStatusReceived(int playerId, SOURCE_ID source, QString artist, QString album, QString Song, QString artwork);
void musicSourcesReceived(QList<MusicSourceObject> musicSources);
void mediaItemsReceived(QList<MediaObject> mediaItems);
void browseRequestReceived(QList<MusicSourceObject> musicSources, QList<MediaObject> mediaItems);
void musicSourcesReceived(QList<MusicSourceObject> musicSources); //callback of getMusicSource, not associated to a playerId
void browseRequestReceived(const QString &sourceId, const QString &containerId, QList<MusicSourceObject> musicSources, QList<MediaObject> mediaItems); //callback of browseSource
void browseErrorReceived(const QString &sourceId, const QString &containerId, int errorId, const QString &errorMessage);
void userChanged(bool signedIn, const QString &userName);
private slots:

View File

@ -93,6 +93,13 @@ enum SEARCH_CRITERIA { // criteria id returned by 'get_search_criteria' com
SEARCH_CRITERIA_STATION
};
enum ADD_CRITERIA {
ADD_CRITERIA_PLAY_NOW = 1,
ADD_CRITERIA_PLAY_NEXT,
ADD_CRITERIA_ADD_TO_END,
ADD_CRITERIA_REPLACE_AND_PLAY
};
enum MEDIA_TYPE {
MEDIA_TYPE_SONG,
MEDIA_TYPE_STATION,

View File

@ -211,6 +211,7 @@ void IntegrationPluginDenon::setupThing(ThingSetupInfo *info)
QHostAddress address(thing->paramValue(heosThingIpParamTypeId).toString());
Heos *heos = new Heos(address, this);
<<<<<<< HEAD:denon/integrationplugindenon.cpp
connect(heos, &Heos::connectionStatusChanged, this, &IntegrationPluginDenon::onHeosConnectionChanged);
connect(heos, &Heos::playerDiscovered, this, &IntegrationPluginDenon::onHeosPlayerDiscovered);
@ -225,6 +226,22 @@ void IntegrationPluginDenon::setupThing(ThingSetupInfo *info)
connect(heos, &Heos::browseRequestReceived, this, &IntegrationPluginDenon::onHeosBrowseRequestReceived);
m_heos.insert(thing->id(), heos);
=======
connect(heos, &Heos::connectionStatusChanged, this, &DevicePluginDenon::onHeosConnectionChanged);
connect(heos, &Heos::playersChanged, this, &DevicePluginDenon::onHeosPlayersChanged);
connect(heos, &Heos::playerDiscovered, this, &DevicePluginDenon::onHeosPlayerDiscovered);
connect(heos, &Heos::playerPlayStateReceived, this, &DevicePluginDenon::onHeosPlayStateReceived);
connect(heos, &Heos::playerRepeatModeReceived, this, &DevicePluginDenon::onHeosRepeatModeReceived);
connect(heos, &Heos::playerShuffleModeReceived, this, &DevicePluginDenon::onHeosShuffleModeReceived);
connect(heos, &Heos::playerMuteStatusReceived, this, &DevicePluginDenon::onHeosMuteStatusReceived);
connect(heos, &Heos::playerVolumeReceived, this, &DevicePluginDenon::onHeosVolumeStatusReceived);
connect(heos, &Heos::nowPlayingMediaStatusReceived, this, &DevicePluginDenon::onHeosNowPlayingMediaStatusReceived);
connect(heos, &Heos::musicSourcesReceived, this, &DevicePluginDenon::onHeosMusicSourcesReceived);
connect(heos, &Heos::browseRequestReceived, this, &DevicePluginDenon::onHeosBrowseRequestReceived);
connect(heos, &Heos::browseErrorReceived, this, &DevicePluginDenon::onHeosBrowseErrorReceived);
m_heos.insert(device->id(), heos);
>>>>>>> improved browsing on TuneIn:denon/deviceplugindenon.cpp
m_asyncHeosSetups.insert(heos, info);
// In case the setup is cancelled before we finish it...
connect(info, &QObject::destroyed, this, [this, info, heos]() { m_asyncHeosSetups.remove(heos); });
@ -590,8 +607,13 @@ void IntegrationPluginDenon::onHeosConnectionChanged(bool status)
}
}
void IntegrationPluginDenon::onHeosPlayerDiscovered(HeosPlayer *heosPlayer) {
void DevicePluginDenon::onHeosPlayersChanged()
{
Heos *heos = static_cast<Heos *>(sender());
heos->getPlayers();
}
void IntegrationPluginDenon::onHeosPlayerDiscovered(HeosPlayer *heosPlayer) {
Heos *heos = static_cast<Heos *>(sender());
Thing *thing = myThings().findById(m_heos.key(heos));
@ -756,13 +778,12 @@ void IntegrationPluginDenon::onHeosNowPlayingMediaStatusReceived(int playerId, S
void IntegrationPluginDenon::onHeosMusicSourcesReceived(QList<MusicSourceObject> musicSources)
{
Heos *heos = static_cast<Heos *>(sender());
if (m_pendingBrowseResult.contains(heos)) {
BrowseResult *result = m_pendingBrowseResult.take(heos);
if (m_pendingGetSourcesRequest.contains(heos)) {
BrowseResult *result = m_pendingGetSourcesRequest.take(heos);
foreach(MusicSourceObject source, musicSources) {
BrowserItem item;
item.setDisplayName(source.name);
//item.setDescription("test");
item.setId(QString::number(source.sourceId));
item.setId("source=" + QString::number(source.sourceId));
item.setThumbnail(source.image_url);
item.setExecutable(false);
item.setBrowsable(true);
@ -774,24 +795,20 @@ void IntegrationPluginDenon::onHeosMusicSourcesReceived(QList<MusicSourceObject>
}
void IntegrationPluginDenon::onHeosMediaItemsReceived(QList<MediaObject> mediaItems)
void DevicePluginDenon::onHeosBrowseRequestReceived(const QString &sourceId, const QString &containerId, QList<MusicSourceObject> musicSources, QList<MediaObject> mediaItems)
{
Heos *heos = static_cast<Heos *>(sender());
if (m_pendingBrowseResult.contains(heos)) {
BrowseResult *result = m_pendingBrowseResult.take(heos);
foreach(MediaObject media, mediaItems) {
BrowserItem item;
item.setDisplayName(media.name);
//item.setDescription("test");
item.setId(media.mediaId);
item.setThumbnail(media.imageUrl);
item.setExecutable(media.isPlayable);
item.setBrowsable(media.isContainer);
result->addItem(item);
qDebug(dcDenon()) << "Media received:" << media.name << media.mediaType << media.mediaId << media.imageUrl;
}
result->finish(Device::DeviceErrorNoError);
QString identifier;
if (containerId.isEmpty()) {
identifier = sourceId;
} else {
identifier = containerId;
}
}*/
if (QUrl(identifier).isValid()) {
identifier = QUrl::fromPercentEncoding(identifier.toUtf8());
}
}
void IntegrationPluginDenon::onHeosBrowseRequestReceived(QList<MusicSourceObject> musicSources, QList<MediaObject> mediaItems)
{
@ -801,26 +818,49 @@ void IntegrationPluginDenon::onHeosBrowseRequestReceived(QList<MusicSourceObject
BrowseResult *result = m_pendingBrowseResult.take(browseRequest);
foreach(MediaObject media, mediaItems) {
BrowserItem item;
qDebug(dcDenon()) << "Adding Item" << media.name << media.mediaId << media.containerId << media.mediaType;
item.setDisplayName(media.name);
//item.setDescription("test");
item.setId(media.mediaId);
if (media.mediaType == MEDIA_TYPE_CONTAINER) {
item.setId("container=" + media.containerId + "&" + sourceId);
} else {
item.setId(media.mediaId);
}
item.setThumbnail(media.imageUrl);
item.setExecutable(media.isPlayable);
item.setBrowsable(media.isContainer);
//item.setActionTypeIds();
result->addItem(item);
qDebug(dcDenon()) << "Media received:" << media.name << media.mediaType << media.mediaId << media.imageUrl;
}
foreach(MusicSourceObject source, musicSources) {
BrowserItem item;
item.setDisplayName(source.name);
qDebug(dcDenon()) << "Adding Item" << source.name << source.sourceId;
//item.setDescription("test");
item.setId(QString::number(source.sourceId));
item.setId("source=" + QString::number(source.sourceId));
item.setThumbnail(source.image_url);
item.setExecutable(true);
item.setExecutable(false);
item.setBrowsable(true);
result->addItem(item);
}
result->finish(Device::DeviceErrorNoError);
} else {
qWarning(dcDenon()) << "Pending browser result doesnt recognize" << identifier << m_pendingBrowseResult.keys();
}
}
void IntegrationPluginDenon::onHeosBrowseErrorReceived(const QString &sourceId, const QString &containerId, int errorId, const QString &errorMessage)
{
QString identifier;
if (containerId.isEmpty()) {
identifier = sourceId;
} else {
identifier = containerId;
}
if (m_pendingBrowseResult.contains(identifier)) {
BrowseResult *result = m_pendingBrowseResult.take(identifier);
qWarning(dcDenon) << "Browse error" << errorMessage << errorId;
result->finish(Device::DeviceErrorHardwareFailure);
}
}
@ -851,6 +891,7 @@ void IntegrationPluginDenon::onPluginConfigurationChanged(const ParamTypeId &par
}
}
void IntegrationPluginDenon::browseDevice(BrowseResult *result)
{
Heos *heos = m_heos.value(result->device()->parentId());
@ -858,16 +899,30 @@ void IntegrationPluginDenon::browseDevice(BrowseResult *result)
result->finish(Device::DeviceErrorHardwareNotAvailable);
return;
}
qDebug(dcDenon()) << "Browse device" << result->itemId() << result->locale();
QUuid requestId;
if (result->itemId().isEmpty()) {
requestId = heos->getMusicSources();
} else {
requestId = heos->browseSource(result->itemId());
qDebug(dcDenon()) << "Browse source";
heos->getMusicSources();
m_pendingGetSourcesRequest.insert(heos, result);
//connect(result, &QObject::destroyed, this, [this, result->itemId()](){ m_pendingBrowseResult.remove(result->itemId());});
} else if (result->itemId().startsWith("source=")){
qDebug(dcDenon()) << "Browse source" << result->itemId();
QString id = result->itemId().remove("source=");
heos->browseSource(id);
m_pendingBrowseResult.insert(id, result);
//connect(result, &QObject::destroyed, this, [this, result->itemId()](){ m_pendingBrowseResult.remove(result->itemId());});
} else if (result->itemId().startsWith("container=")){
qDebug(dcDenon()) << "Browse container" << result->itemId();
QStringList values = result->itemId().split("&");
if (values.length() == 2) {
QString id = values[0].remove("container=");
heos->browseSourceContainers(values[1], id);
if (QUrl(id).isValid()) {
id = QUrl::fromPercentEncoding(id.toUtf8());
}
m_pendingBrowseResult.insert(id, result);
}
}
m_pendingBrowseResult.insert(requestId, result);
connect(result, &QObject::destroyed, this, [this, requestId](){ m_pendingBrowseResult.remove(requestId);});
}
void IntegrationPluginDenon::browserItem(BrowserItemResult *result)
@ -877,7 +932,6 @@ void IntegrationPluginDenon::browserItem(BrowserItemResult *result)
result->finish(Device::DeviceErrorHardwareNotAvailable);
return;
}
qDebug(dcDenon()) << "Browse item called";
return;
}
@ -889,16 +943,22 @@ void IntegrationPluginDenon::executeBrowserItem(BrowserActionInfo *info)
info->finish(Device::DeviceErrorHardwareNotAvailable);
return;
}
qDebug(dcDenon()) << "Execute browse item called";
return;
/*
int id = kodi->launchBrowserItem(info->browserAction().itemId());
if (id == -1) {
return info->finish(Device::DeviceErrorHardwareFailure);
/* BrowserAction action = info->browserAction();
int playerId = info->device()->paramValue(heosPlayerDevicePlayerIdParamTypeId).toInt();
if (action.itemId()) {
heos->playUrl(playerId, action.itemId());
} else {
heos->playPresetStation(playerId, presetNumber);
}
m_pendingBrowserActions.insert(id, info);
connect(info, &QObject::destroyed, this, [this, id](){ m_pendingBrowserActions.remove(id); });*/
heos->playInputSource(playerId, inputName);
*/
qDebug(dcDenon()) << "Execute browse item called";
info->finish(Device::DeviceErrorNoError);
return;
}
void IntegrationPluginDenon::executeBrowserItemAction(BrowserItemActionInfo *info)
@ -908,12 +968,6 @@ void IntegrationPluginDenon::executeBrowserItemAction(BrowserItemActionInfo *inf
info->finish(Device::DeviceErrorHardwareNotAvailable);
return;
}
/*int id = kodi->executeBrowserItemAction(info->browserItemAction().itemId(), info->browserItemAction().actionTypeId());
if (id == -1) {
return info->finish(Device::DeviceErrorHardwareFailure);
}
m_pendingBrowserItemActions.insert(id, info);
connect(info, &QObject::destroyed, this, [this, id](){ m_pendingBrowserItemActions.remove(id); });*/
qDebug(dcDenon()) << "Execute browse item action called";
return;
}

View File

@ -84,8 +84,9 @@ private:
QHash<const Action *, int> m_asyncActions;
QUrl m_notificationUrl;
QHash<Heos *, BrowseResult*> m_pendingBrowseResult;
QHash<int, ThingActionInfo*> m_pendingActions;
QHash<Heos*, BrowseResult*> m_pendingGetSourcesRequest;
QHash<QString, BrowseResult*> m_pendingBrowseResult; // QString = containerId or sourceId
QHash<int, BrowserActionInfo*> m_pendingBrowserActions;
QHash<int, BrowserItemActionInfo*> m_pendingBrowserItemActions;
@ -93,6 +94,7 @@ private slots:
void onPluginTimer();
void onHeosConnectionChanged(bool status);
void onHeosPlayersChanged();
void onHeosPlayerDiscovered(HeosPlayer *heosPlayer);
void onHeosPlayStateReceived(int playerId, PLAYER_STATE state);
void onHeosShuffleModeReceived(int playerId, bool shuffle);
@ -100,9 +102,10 @@ private slots:
void onHeosMuteStatusReceived(int playerId, bool mute);
void onHeosVolumeStatusReceived(int playerId, int volume);
void onHeosNowPlayingMediaStatusReceived(int playerId, SOURCE_ID source, QString artist, QString album, QString Song, QString artwork);
//void onHeosMusicSourcesReceived(QList<MusicSourceObject> musicSources);
void onHeosMusicSourcesReceived(QList<MusicSourceObject> musicSources);
//void onHeosMediaItemsReceived(QList<MediaObject> mediaItems);
void onHeosBrowseRequestReceived(QList<MusicSourceObject> musicSources, QList<MediaObject> mediaItems);
void onHeosBrowseRequestReceived(const QString &sourceId, const QString &containerId, QList<MusicSourceObject> musicSources, QList<MediaObject> mediaItems);
void onHeosBrowseErrorReceived(const QString &sourceId, const QString &containerId, int errorId, const QString &errorMessage);
void onHeosPlayerNowPlayingChanged(int playerId);
void onAvahiServiceEntryAdded(const ZeroConfServiceEntry &serviceEntry);