123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- //
- // Created by Tobias Hieta on 31/03/15.
- //
- #include <QNetworkAccessManager>
- #include <QsLog.h>
- #include <QtCore/qxmlstream.h>
- #include <QDomDocument>
- #include <QMutex>
- #include "RemoteSubscriber.h"
- #include "RemoteComponent.h"
- #include "settings/SettingsComponent.h"
- /////////////////////////////////////////////////////////////////////////////////////////
- RemoteSubscriber::RemoteSubscriber(const QString& clientIdentifier, const QString& deviceName, const QUrl& address, QObject* parent)
- : QObject(parent), m_address(address), m_clientIdentifier(clientIdentifier), m_deviceName(deviceName)
- {
- m_subscribeTime.start();
- if (!address.isEmpty())
- {
- RemoteComponent* remote = dynamic_cast<RemoteComponent*>(parent);
- Q_ASSERT(remote);
- m_netAccess = remote->getNetworkAccessManager();
- // make first access faster by connecting directly to the host now.
- if (address.scheme() == "https")
- m_netAccess->connectToHostEncrypted(address.host(), address.port());
- else
- m_netAccess->connectToHost(address.host(), address.port());
- }
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- void RemoteSubscriber::reSubscribe()
- {
- m_subscribeTime.restart();
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- QString RemoteSubscriber::deviceName()
- {
- return m_deviceName;
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- QString RemoteSubscriber::clientIdentifier()
- {
- return m_clientIdentifier;
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- void RemoteSubscriber::sendUpdate()
- {
- QUrl url(m_address);
- url.setPath("/:/timeline");
- QNetworkRequest request(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
- request.setAttribute(QNetworkRequest::User, m_clientIdentifier);
- QVariantMap headers = RemoteComponent::HeaderInformation();
- foreach (const QString& key, headers.keys())
- {
- request.setRawHeader(key.toUtf8(), headers[key].toString().toUtf8());
- }
- m_netAccess->post(request, getTimeline());
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- void RemoteSubscriber::timelineFinished(QNetworkReply* reply)
- {
- if (reply->error() != QNetworkReply::NoError)
- {
- QLOG_ERROR() << "got error code when sending timeline:" << reply->errorString();
- #if 0
- if (++m_errors > 10)
- {
- QLOG_ERROR() << "More than 10 errors received. Dropping this subscriber";
- RemoteComponent::Get()->subscriberRemove(clientIdentifier());
- }
- #endif
- }
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- void RemoteSubscriber::queueTimeline(quint64 playerCommandID, const QByteArray& timelineData)
- {
- QMutexLocker lk(&m_timelineLock);
- QDomDocument doc;
- if (doc.setContent(timelineData) && !doc.firstChildElement("MediaContainer").isNull())
- {
- QDomElement node = doc.firstChildElement("MediaContainer");
- node.setAttribute("commandID", commandId(playerCommandID));
- m_timeline = doc;
- }
- else
- QLOG_WARN() << "Failed to parse timeline data from player";
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- QByteArray RemoteSubscriber::getTimeline()
- {
- QMutexLocker lk(&m_timelineLock);
- if (m_timeline.isNull())
- {
- QByteArray xmlData;
- QXmlStreamWriter writer(&xmlData);
- writer.writeStartDocument();
- writer.writeStartElement("MediaContainer");
- writer.writeAttribute("location", "navigation");
- writer.writeAttribute("commandID", QString::number(mostRecentCommandId()));
- writer.writeStartElement("Timeline");
- writer.writeAttribute("type", "music");
- writer.writeAttribute("state", "stopped");
- writer.writeEndElement();
- writer.writeStartElement("Timeline");
- writer.writeAttribute("type", "video");
- writer.writeAttribute("state", "stopped");
- writer.writeEndElement();
- writer.writeStartElement("Timeline");
- writer.writeAttribute("type", "photo");
- writer.writeAttribute("state", "stopped");
- writer.writeEndElement();
- writer.writeEndElement();
- writer.writeEndDocument();
- return xmlData;
- }
- else
- {
- return m_timeline.toByteArray(2);
- }
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- void RemoteSubscriber::setCommandId(quint64 playerCommandId, quint64 controllerCommandId)
- {
- QMutexLocker lk(&m_commandIdMapLock);
- m_commandIdMap[playerCommandId] = controllerCommandId;
- // maintain a queue of playerCommandId's so we know in what order to
- // drop them if we get a lot of them. before we queue the current one
- // make sure that we remove any old reference to it so it won't get
- // deleted from the map to early
- //
- m_commandIdQueue.removeAll(playerCommandId);
- m_commandIdQueue.enqueue(playerCommandId);
- if (m_commandIdQueue.size() > 5)
- {
- // remove the last one
- quint64 keyToRemove = m_commandIdQueue.dequeue();
- m_commandIdMap.remove(keyToRemove);
- }
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- RemotePollSubscriber::RemotePollSubscriber(const QString &clientIdentifier, const QString &deviceName, QHttpResponse *response, QObject *parent) :
- RemoteSubscriber(clientIdentifier, deviceName, QUrl(""), parent), m_response(response)
- {
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- void RemotePollSubscriber::setHTTPResponse(QHttpResponse *response)
- {
- m_response = response;
- m_response->addHeader("Content-Type", "application/xml");
- m_response->addHeader("Access-Control-Expose-Headers", "X-Plex-Client-Identifier");
- connect(m_response, &QHttpResponse::done, this, &RemotePollSubscriber::responseDone);
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- void RemotePollSubscriber::sendUpdate()
- {
- if (m_response)
- {
- // if we have a response, we are handling a poll request
- m_response->setStatusCode(qhttp::ESTATUS_OK);
- m_response->write(getTimeline());
- m_response->end();
- m_response = nullptr;
- }
- }
- /////////////////////////////////////////////////////////////////////////////////////////
- void RemotePollSubscriber::responseDone()
- {
- m_response = nullptr;
- }
|