TTKMusicPlayer  4.3.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
qdlnaclient.cpp
Go to the documentation of this file.
1 #include "qdlnaclient.h"
2 #include "qdlnaxml.h"
3 
4 #include <QMap>
5 #include <QUrl>
6 #include <unistd.h>
7 #include <QFileInfo>
8 #include <QStringList>
9 
10 static constexpr const char *AVTRANSPORT = "avtransport";
11 static const QString AVTSCHEMA = "urn:schemas-upnp-org:service:AVTransport:1";
12 
13 static const QString XML_HEAD = "<?xml version='1.0' encoding='UTF-8'?>\n<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n<SOAP-ENV:Body>\n";
14 static const QString XML_FOOT = "</SOAP-ENV:Body>\n</SOAP-ENV:Envelope>\n";
15 static const QString META_DATA = "&lt;DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\""
16  " xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\""
17  " xmlns:dc=\"http://purl.org/dc/elements/1.1/\""
18  " xmlns:sec=\"http://www.sec.co.kr/\"&gt;&lt;item id=\"f-0\" parentID=\"0\" restricted=\"0\"&gt;"
19  "&lt;dc:title&gt;%1&lt;/dc:title&gt;"
20  "&lt;dc:creator&gt;%2&lt;/dc:creator&gt;"
21  "&lt;upnp:class&gt;object.item.%3Item&lt;/upnp:class&gt;"
22  "&lt;res protocolInfo=\"http-get:*:%4:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000\" &gt;"
23  "%5&lt;/res&gt;&lt;/item&gt;&lt;/DIDL-Lite&gt;";
24 static const QStringList AVT_NAMES = {"avtransport", "AVTransport"};
25 static const QStringList FRIENS_NAMES = {"friendlyname", "friendlyName", "FriendlyName", "FriendlyName"};
26 
30 class QDlnaClientPrivate : public TTKPrivate<QDlnaClient>
31 {
32 public:
33  QDlnaClientPrivate() noexcept;
34 
35  void initialize(const QString &data);
36  bool connect();
37 
39 
40  QString m_address, m_port;
41  QString m_smp, m_controlURL;
42  QString m_friendlyName;
43  QMap<QString, QDlnaService> m_services;
44 
45 };
46 
48  : m_isConnected(false)
49 {
50 
51 }
52 
53 void QDlnaClientPrivate::initialize(const QString &data)
54 {
55  const QStringList &datas = data.split(TTK_WLINEFEED);
56  for(QString str : qAsConst(datas))
57  {
58  QStringList locations;
59  if(str.contains("LOCATION:"))
60  {
61  locations = str.remove("LOCATION: http://").split(":");
62  }
63  else if(str.contains("Location:"))
64  {
65  locations = str.remove("Location: http://").split(":");
66  }
67  else if(str.contains("location:"))
68  {
69  locations = str.remove("location: http://").split(":");
70  }
71 
72  if(locations.count() >= 2)
73  {
74  m_address = locations[0];
75  const QStringList &infos = locations[1].split(TTK_SEPARATOR);
76 
77  if(infos.count() >= 2)
78  {
79  m_port = infos[0];
80  m_smp = infos[1];
81  }
82  }
83  }
84 }
85 
87 {
88  const QString &request = QDlna::makeRequest("GET", m_smp, 0, {}, m_address, m_port);
89  const QString &response = QDlna::makeSocketGetReply(m_address, m_port, request);
90  TTK_INFO_STREAM(m_address << m_port << m_smp << response);
91 
92  if(!QDlna::isValid(response))
93  {
94  return false;
95  }
96 
97  QDlnaXml xml;
98  if(!xml.fromString(xml.tagNameToLower(QDlna::removeHttpHeader(response))))
99  {
100  return false;
101  }
102  //
103  for(const QString &name : qAsConst(FRIENS_NAMES))
104  {
106  if(!m_friendlyName.isEmpty())
107  {
108  break;
109  }
110  }
111  //
112  if(m_friendlyName.isEmpty())
113  {
114  return false;
115  }
116  //
117  for(const QString &name : qAsConst(AVT_NAMES))
118  {
119  const QDlnaService &server = xml.readServiceTag(name, "server");
120  if(!server.isEmpty())
121  {
122  m_services.insert(AVTRANSPORT, server);
123  break;
124  }
125  }
126  //
127  for(const QString &name : qAsConst(AVT_NAMES))
128  {
129  const QDlnaService &server = xml.readServiceTag(name, "service");
130  if(!server.isEmpty())
131  {
132  m_services.insert(AVTRANSPORT, server);
133  break;
134  }
135  }
136  //
137  for(auto it = m_services.begin(); it != m_services.end(); ++it)
138  {
139  if(it.key().contains(AVTRANSPORT))
140  {
141  m_controlURL = it.value().m_controlURL;
142  m_isConnected = true;
143  return true;
144  }
145  }
146  return false;
147 }
148 
149 
150 
151 QDlnaClient::QDlnaClient(const QString &data)
152 {
155  d->initialize(data);
156 }
157 
158 QString QDlnaClient::server() const
159 {
160  TTK_D(const QDlnaClient);
161  return d->m_address;
162 }
163 
164 QString QDlnaClient::serverName() const
165 {
166  TTK_D(const QDlnaClient);
167  return d->m_friendlyName;
168 }
169 
171 {
173  return d->connect();
174 }
175 
177 {
178  TTK_D(const QDlnaClient);
179  return d->m_isConnected;
180 }
181 
182 bool QDlnaClient::openUri(const QString &url, const QString &name, int instance) const
183 {
184  TTK_D(const QDlnaClient);
185  //Later we will send a message to the DLNA server to start the file playing
186  const QFileInfo fin(url + name);
187  QString play_url = url + QUrl::toPercentEncoding(name);
188  QString body = XML_HEAD;
189 
190  body += "<u:SetAVTransportURI xmlns:u=\""+ AVTSCHEMA + "\">\n";
191  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
192  body += "<CurrentURI>" + play_url + "</CurrentURI>\n";
193  body += "<CurrentURIMetaData>" + META_DATA.arg(fin.baseName(), fin.owner(), "audio", "audio/mp3", play_url) + "</CurrentURIMetaData>\n";
194  body += "</u:SetAVTransportURI>\n";
195  body += XML_FOOT + "\n";
196 
197  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#SetAVTransportURI", d->m_address,d->m_port) + body;
198  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
199 }
200 
201 bool QDlnaClient::openUris(const QString &url, const QString &name, int instance) const
202 {
203  TTK_D(const QDlnaClient);
204  //Later we will send a message to the DLNA server to start the file playing
205  const QFileInfo fin(url + name);
206  QString play_url = url + QUrl::toPercentEncoding(name);
207  QString body = XML_HEAD;
208 
209  body += "<u:AddURIToQueue xmlns:u=\""+ AVTSCHEMA + "\">\n";
210  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
211  body += "<EnqueuedURI>" + play_url + "</EnqueuedURI>\n";
212  body += "<EnqueuedURIMetaData>" + META_DATA.arg(fin.baseName(), fin.owner(), "audio", "audio/mp3", play_url) + "</EnqueuedURIMetaData>\n";
213  body += "<DesiredFirstTrackNumberEnqueued>0</DesiredFirstTrackNumberEnqueued>\n";
214  body += "<EnqueueAsNext>1</EnqueueAsNext>\n";
215  body += "</u:AddURIToQueue>\n";
216  body += XML_FOOT + "\n";
217 
218  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#AddURIToQueue", d->m_address,d->m_port) + body;
219  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
220 }
221 
222 bool QDlnaClient::nextUri(const QString &url, const QString &name, int instance) const
223 {
224  TTK_D(const QDlnaClient);
225  //Later we will send a message to the DLNA server to next start the file playing
226  const QFileInfo fin(url + name);
227  QString play_url = url + QUrl::toPercentEncoding(name);
228  QString body = XML_HEAD;
229 
230  body += "<u:SetNextAVTransportURI xmlns:u=\""+ AVTSCHEMA + "\">\n";
231  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
232  body += "<NextURI>" + play_url + "</NextURI>\n";
233  body += "<NextURIMetaData>" + META_DATA.arg(fin.baseName(), fin.owner(), "audio", "audio/mp3", play_url) + "</NextURIMetaData>\n";
234  body += "</u:SetNextAVTransportURI>\n";
235  body += XML_FOOT + "\n";
236 
237  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#SetNextAVTransportURI", d->m_address,d->m_port) + body;
238  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
239 }
240 
241 static qint64 valueToSecond(const QString &value)
242 {
243  return QDateTime::fromString(value, TTK_TIMES_FORMAT).toMSecsSinceEpoch() / 1000;
244 }
245 
246 bool QDlnaClient::positionInfo(QDlna::PositionInfo &info, int instance) const
247 {
248  TTK_D(const QDlnaClient);
249  //Returns the current position info
250  QString body = XML_HEAD;
251  body += "<u:GetPositionInfo xmlns:u=\""+ AVTSCHEMA + "\">\n";
252  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
253  body += "</u:GetPositionInfo>\n";
254  body += XML_FOOT + "\n";
255 
256  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#GetPositionInfo", d->m_address, d->m_port) + body;
257  const QString &response = QDlna::makeSocketGetReply(d->m_address, d->m_port, request);
258  if(!QDlna::isValid(response))
259  {
260  return false;
261  }
262 
263  QDlnaXml xml;
264  if(!xml.fromString(QDlna::removeHttpHeader(response)))
265  {
266  return false;
267  }
268 
269  info.position = valueToSecond(xml.readTagNameValue("Relime"));
270  info.duration = valueToSecond(xml.readTagNameValue("TrackDuration"));
271  return true;
272 }
273 
274 bool QDlnaClient::transportInfo(QDlna::TransportInfo &info, int instance) const
275 {
276  TTK_D(const QDlnaClient);
277  //Returns the current transport info
278  QString body = XML_HEAD;
279  body += "<u:GetTransportInfo xmlns:u=\""+ AVTSCHEMA + "\">\n";
280  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
281  body += "</u:GetTransportInfo>\n";
282  body += XML_FOOT + "\n";
283 
284  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#GetTransportInfo", d->m_address, d->m_port) + body;
285  const QString &response = QDlna::makeSocketGetReply(d->m_address, d->m_port, request);
286  if(!QDlna::isValid(response))
287  {
288  return false;
289  }
290 
291  QDlnaXml xml;
292  if(!xml.fromString(QDlna::removeHttpHeader(response)))
293  {
294  return false;
295  }
296 
297  info.state = xml.readTagNameValue("CurrentTransportState");
298  info.status = xml.readTagNameValue("CurrentTransportStatus");
299  info.speed = xml.readTagNameValue("CurrentSpeed").toInt();
300  return true;
301 }
302 
303 bool QDlnaClient::mediaInfo(QDlna::MediaInfo &info, int instance) const
304 {
305  TTK_D(const QDlnaClient);
306  //Returns the current media info
307  QString body = XML_HEAD;
308  body += "<u:GetMediaInfo xmlns:u=\""+ AVTSCHEMA + "\">\n";
309  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
310  body += "</u:GetMediaInfo>\n";
311  body += XML_FOOT + "\n";
312 
313  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#GetMediaInfo", d->m_address, d->m_port) + body;
314  const QString &response = QDlna::makeSocketGetReply(d->m_address, d->m_port, request);
315  if(!QDlna::isValid(response))
316  {
317  return false;
318  }
319 
320  QDlnaXml xml;
321  if(!xml.fromString(QDlna::removeHttpHeader(response)))
322  {
323  return false;
324  }
325 
326  info.duration = xml.readTagNameValue("MediaDuration");
327  info.nextURI = xml.readTagNameValue("NextURI");
328  info.medium = xml.readTagNameValue("PlayMedium");
329  return true;
330 }
331 
332 bool QDlnaClient::play(int instance) const
333 {
334  TTK_D(const QDlnaClient);
335  //Start playing the new upload track
336  QString body = XML_HEAD;
337  body += "<u:Play xmlns:u=\""+ AVTSCHEMA + "\">\n";
338  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
339  body += "<Speed>1</Speed>\n";
340  body += "</u:Play>\n";
341  body += XML_FOOT + "\n";
342 
343  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#Play", d->m_address, d->m_port) + body;
344  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
345 }
346 
347 bool QDlnaClient::pause(int instance) const
348 {
349  TTK_D(const QDlnaClient);
350  //Called to pause playing track
351  QString body = XML_HEAD;
352  body += "<u:Pause xmlns:u=\""+ AVTSCHEMA + "\">\n";
353  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
354  body += "</u:Pause>\n";
355  body += XML_FOOT + "\n";
356 
357  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#Pause", d->m_address, d->m_port) + body;
358  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
359 }
360 
361 bool QDlnaClient::stop(int instance) const
362 {
363  TTK_D(const QDlnaClient);
364  //Called to stop playing track
365  QString body = XML_HEAD;
366  body += "<u:Stop xmlns:u=\""+ AVTSCHEMA + "\">\n";
367  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
368  body += "</u:Stop>\n";
369  body += XML_FOOT + "\n";
370 
371  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#Stop", d->m_address, d->m_port) + body;
372  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
373 }
374 
375 bool QDlnaClient::remove(int instance) const
376 {
377  TTK_D(const QDlnaClient);
378  //Called to remove queue track
379  QString body = XML_HEAD;
380  body += "<u:RemoveAllTracksFromQueue xmlns:u=\""+ AVTSCHEMA + "\">\n";
381  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
382  body += "</u:RemoveAllTracksFromQueue>\n";
383  body += XML_FOOT + "\n";
384 
385  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#RemoveAllTracksFromQueue", d->m_address, d->m_port) + body;
386  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
387 }
388 
389 bool QDlnaClient::previous(int instance) const
390 {
391  TTK_D(const QDlnaClient);
392  //Called to previous queue track
393  QString body = XML_HEAD;
394  body += "<u:Previous xmlns:u=\""+ AVTSCHEMA + "\">\n";
395  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
396  body += "</u:Previous>\n";
397  body += XML_FOOT + "\n";
398 
399  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#Previous", d->m_address, d->m_port) + body;
400  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
401 }
402 
403 bool QDlnaClient::next(int instance) const
404 {
405  TTK_D(const QDlnaClient);
406  //Called to next queue track
407  QString body = XML_HEAD;
408  body += "<u:Next xmlns:u=\""+ AVTSCHEMA + "\">\n";
409  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
410  body += "</u:Next>\n";
411  body += XML_FOOT + "\n";
412 
413  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#Next", d->m_address, d->m_port) + body;
414  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
415 }
416 
417 bool QDlnaClient::setPlayMode(const QString &mode, int instance) const
418 {
419  TTK_D(const QDlnaClient);
420  //Called to set play mode
421  // allowed NewPlayMode = "NORMAL", "REPEAT_ONE", "REPEAT_ALL", "RANDOM"
422  QString body = XML_HEAD;
423  body += "<u:SetPlayMode xmlns:u=\""+ AVTSCHEMA + "\">\n";
424  body += "<InstanceID>" + QString::number(instance) + "</InstanceID>\n";
425  body += "<NewPlayMode>" + mode + "</NewPlayMode>\n";
426  body += "</u:SetPlayMode>\n";
427  body += XML_FOOT + "\n";
428 
429  const QString &request = QDlna::makeRequest("POST", d->m_controlURL, body.length(), AVTSCHEMA + "#SetPlayMode", d->m_address,d->m_port) + body;
430  return QDlna::isValid(QDlna::makeSocketGetReply(d->m_address, d->m_port, request));
431 }
QString status
Definition: qdlna.h:38
bool isValid(const QString &data)
Definition: qdlna.cpp:76
bool remove(int instance=0) const
#define TTK_TIMES_FORMAT
Definition: ttkglobal.h:303
QMap< QString, QDlnaService > m_services
Definition: qdlnaclient.cpp:43
bool pause(int instance=0) const
QString makeRequest(const QString &methord, const QString &url, int length, const QString &soapAction, const QString &ip, const QString &port)
Definition: qdlna.cpp:39
QString removeHttpHeader(const QString &data)
Definition: qdlna.cpp:70
The class of the dlna client.
Definition: qdlnaclient.h:35
static const QStringList AVT_NAMES
Definition: qdlnaclient.cpp:24
bool next(int instance=0) const
QString tagNameToLower(const QString &data) const
Definition: qdlnaxml.cpp:56
The class of the dlna xml.
Definition: qdlnaxml.h:29
qint64 duration
Definition: qdlna.h:32
QString readTagNameValue(const QString &tagName) const
Definition: qdlnaxml.cpp:75
QString makeSocketGetReply(const QString &ip, const QString &port, const QString &data)
Definition: qdlna.cpp:9
#define qAsConst
Definition: ttkqtglobal.h:57
bool openUri(const QString &url, const QString &name, int instance=0) const
const char * name
Definition: http_parser.c:458
bool nextUri(const QString &url, const QString &name, int instance=0) const
QString duration
Definition: qdlna.h:44
bool openUris(const QString &url, const QString &name, int instance=0) const
bool play(int instance=0) const
#define TTK_INFO_STREAM(msg)
Definition: ttklogger.h:74
bool connect() const
static const QString XML_FOOT
Definition: qdlnaclient.cpp:14
bool transportInfo(QDlna::TransportInfo &info, int instance=0) const
bool mediaInfo(QDlna::MediaInfo &info, int instance=0) const
QString state
Definition: qdlna.h:37
qint64 position
Definition: qdlna.h:31
#define TTK_SEPARATOR
Definition: ttkglobal.h:269
static qint64 valueToSecond(const QString &value)
QDlnaClientPrivate() noexcept
Definition: qdlnaclient.cpp:47
static const QString AVTSCHEMA
Definition: qdlnaclient.cpp:11
The class of the dlna service.
Definition: qdlnaservice.h:27
static const QStringList FRIENS_NAMES
Definition: qdlnaclient.cpp:25
void initialize(const QString &data)
Definition: qdlnaclient.cpp:53
The class of the dlna client private.
Definition: qdlnaclient.cpp:30
static const QString XML_HEAD
Definition: qdlnaclient.cpp:13
static const QString META_DATA
Definition: qdlnaclient.cpp:15
bool isEmpty() const noexcept
Definition: qdlnaservice.h:35
#define TTK_WLINEFEED
Definition: ttkglobal.h:272
QDlnaService readServiceTag(const QString &type, const QString &tagName) const
Definition: qdlnaxml.cpp:82
bool previous(int instance=0) const
QString server() const
const char int mode
Definition: ioapi.h:135
bool stop(int instance=0) const
#define TTK_INIT_PRIVATE(Class)
Definition: ttkprivate.h:33
bool setPlayMode(const QString &mode, int instance) const
The class of the ttk private base.
Definition: ttkprivate.h:48
bool positionInfo(QDlna::PositionInfo &info, int instance=0) const
QString medium
Definition: qdlna.h:46
static constexpr const char * AVTRANSPORT
Definition: qdlnaclient.cpp:10
QDlnaClient(const QString &data)
bool isConnected() const
QString serverName() const
bool fromString(const QString &data)
Definition: qdlnaxml.cpp:36
QString nextURI
Definition: qdlna.h:45
#define TTK_D(Class)
Definition: ttkprivate.h:41