RemoteSubscriber.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. //
  2. // Created by Tobias Hieta on 31/03/15.
  3. //
  4. #include <QNetworkAccessManager>
  5. #include <QsLog.h>
  6. #include <QtCore/qxmlstream.h>
  7. #include <QDomDocument>
  8. #include <QMutex>
  9. #include "RemoteSubscriber.h"
  10. #include "RemoteComponent.h"
  11. #include "settings/SettingsComponent.h"
  12. /////////////////////////////////////////////////////////////////////////////////////////
  13. RemoteSubscriber::RemoteSubscriber(const QString& clientIdentifier, const QString& deviceName, const QUrl& address, QObject* parent)
  14. : QObject(parent), m_address(address), m_clientIdentifier(clientIdentifier), m_deviceName(deviceName)
  15. {
  16. m_subscribeTime.start();
  17. if (!address.isEmpty())
  18. {
  19. RemoteComponent* remote = dynamic_cast<RemoteComponent*>(parent);
  20. Q_ASSERT(remote);
  21. m_netAccess = remote->getNetworkAccessManager();
  22. // make first access faster by connecting directly to the host now.
  23. if (address.scheme() == "https")
  24. m_netAccess->connectToHostEncrypted(address.host(), address.port());
  25. else
  26. m_netAccess->connectToHost(address.host(), address.port());
  27. }
  28. }
  29. /////////////////////////////////////////////////////////////////////////////////////////
  30. void RemoteSubscriber::reSubscribe()
  31. {
  32. m_subscribeTime.restart();
  33. }
  34. /////////////////////////////////////////////////////////////////////////////////////////
  35. QString RemoteSubscriber::deviceName()
  36. {
  37. return m_deviceName;
  38. }
  39. /////////////////////////////////////////////////////////////////////////////////////////
  40. QString RemoteSubscriber::clientIdentifier()
  41. {
  42. return m_clientIdentifier;
  43. }
  44. /////////////////////////////////////////////////////////////////////////////////////////
  45. void RemoteSubscriber::sendUpdate()
  46. {
  47. QUrl url(m_address);
  48. url.setPath("/:/timeline");
  49. QNetworkRequest request(url);
  50. request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
  51. request.setAttribute(QNetworkRequest::User, m_clientIdentifier);
  52. QVariantMap headers = RemoteComponent::HeaderInformation();
  53. foreach (const QString& key, headers.keys())
  54. {
  55. request.setRawHeader(key.toUtf8(), headers[key].toString().toUtf8());
  56. }
  57. m_netAccess->post(request, getTimeline());
  58. }
  59. /////////////////////////////////////////////////////////////////////////////////////////
  60. void RemoteSubscriber::timelineFinished(QNetworkReply* reply)
  61. {
  62. if (reply->error() != QNetworkReply::NoError)
  63. {
  64. QLOG_ERROR() << "got error code when sending timeline:" << reply->errorString();
  65. #if 0
  66. if (++m_errors > 10)
  67. {
  68. QLOG_ERROR() << "More than 10 errors received. Dropping this subscriber";
  69. RemoteComponent::Get()->subscriberRemove(clientIdentifier());
  70. }
  71. #endif
  72. }
  73. }
  74. /////////////////////////////////////////////////////////////////////////////////////////
  75. void RemoteSubscriber::queueTimeline(quint64 playerCommandID, const QByteArray& timelineData)
  76. {
  77. QMutexLocker lk(&m_timelineLock);
  78. QDomDocument doc;
  79. if (doc.setContent(timelineData) && !doc.firstChildElement("MediaContainer").isNull())
  80. {
  81. QDomElement node = doc.firstChildElement("MediaContainer");
  82. node.setAttribute("commandID", commandId(playerCommandID));
  83. m_timeline = doc;
  84. }
  85. else
  86. QLOG_WARN() << "Failed to parse timeline data from player";
  87. }
  88. /////////////////////////////////////////////////////////////////////////////////////////
  89. QByteArray RemoteSubscriber::getTimeline()
  90. {
  91. QMutexLocker lk(&m_timelineLock);
  92. if (m_timeline.isNull())
  93. {
  94. QByteArray xmlData;
  95. QXmlStreamWriter writer(&xmlData);
  96. writer.writeStartDocument();
  97. writer.writeStartElement("MediaContainer");
  98. writer.writeAttribute("location", "navigation");
  99. writer.writeAttribute("commandID", QString::number(mostRecentCommandId()));
  100. writer.writeStartElement("Timeline");
  101. writer.writeAttribute("type", "music");
  102. writer.writeAttribute("state", "stopped");
  103. writer.writeEndElement();
  104. writer.writeStartElement("Timeline");
  105. writer.writeAttribute("type", "video");
  106. writer.writeAttribute("state", "stopped");
  107. writer.writeEndElement();
  108. writer.writeStartElement("Timeline");
  109. writer.writeAttribute("type", "photo");
  110. writer.writeAttribute("state", "stopped");
  111. writer.writeEndElement();
  112. writer.writeEndElement();
  113. writer.writeEndDocument();
  114. return xmlData;
  115. }
  116. else
  117. {
  118. return m_timeline.toByteArray(2);
  119. }
  120. }
  121. /////////////////////////////////////////////////////////////////////////////////////////
  122. void RemoteSubscriber::setCommandId(quint64 playerCommandId, quint64 controllerCommandId)
  123. {
  124. QMutexLocker lk(&m_commandIdMapLock);
  125. m_commandIdMap[playerCommandId] = controllerCommandId;
  126. // maintain a queue of playerCommandId's so we know in what order to
  127. // drop them if we get a lot of them. before we queue the current one
  128. // make sure that we remove any old reference to it so it won't get
  129. // deleted from the map to early
  130. //
  131. m_commandIdQueue.removeAll(playerCommandId);
  132. m_commandIdQueue.enqueue(playerCommandId);
  133. if (m_commandIdQueue.size() > 5)
  134. {
  135. // remove the last one
  136. quint64 keyToRemove = m_commandIdQueue.dequeue();
  137. m_commandIdMap.remove(keyToRemove);
  138. }
  139. }
  140. /////////////////////////////////////////////////////////////////////////////////////////
  141. RemotePollSubscriber::RemotePollSubscriber(const QString &clientIdentifier, const QString &deviceName, QHttpResponse *response, QObject *parent) :
  142. RemoteSubscriber(clientIdentifier, deviceName, QUrl(""), parent), m_response(response)
  143. {
  144. }
  145. /////////////////////////////////////////////////////////////////////////////////////////
  146. void RemotePollSubscriber::setHTTPResponse(QHttpResponse *response)
  147. {
  148. m_response = response;
  149. m_response->addHeader("Content-Type", "application/xml");
  150. m_response->addHeader("Access-Control-Expose-Headers", "X-Plex-Client-Identifier");
  151. connect(m_response, &QHttpResponse::done, this, &RemotePollSubscriber::responseDone);
  152. }
  153. /////////////////////////////////////////////////////////////////////////////////////////
  154. void RemotePollSubscriber::sendUpdate()
  155. {
  156. if (m_response)
  157. {
  158. // if we have a response, we are handling a poll request
  159. m_response->setStatusCode(qhttp::ESTATUS_OK);
  160. m_response->write(getTimeline());
  161. m_response->end();
  162. m_response = nullptr;
  163. }
  164. }
  165. /////////////////////////////////////////////////////////////////////////////////////////
  166. void RemotePollSubscriber::responseDone()
  167. {
  168. m_response = nullptr;
  169. }