|
a/libupnpp/cdircontent.cxx |
|
b/libupnpp/cdircontent.cxx |
|
... |
|
... |
18 |
|
18 |
|
19 |
#include <string>
|
19 |
#include <string>
|
20 |
#include <vector>
|
20 |
#include <vector>
|
21 |
#include <iostream>
|
21 |
#include <iostream>
|
22 |
#include <map>
|
22 |
#include <map>
|
23 |
#include <set>
|
23 |
|
24 |
using std::string;
|
24 |
using namespace std;
|
25 |
using std::cerr;
|
|
|
26 |
using std::endl;
|
|
|
27 |
using std::vector;
|
|
|
28 |
using std::map;
|
|
|
29 |
using std::set;
|
|
|
30 |
|
25 |
|
31 |
#include "expatmm.hxx"
|
26 |
#include "expatmm.hxx"
|
32 |
#include "upnpp_p.hxx"
|
27 |
#include "upnpp_p.hxx"
|
33 |
#include "cdircontent.hxx"
|
28 |
#include "cdircontent.hxx"
|
|
|
29 |
#include "log.hxx"
|
34 |
|
30 |
|
35 |
#if 0
|
|
|
36 |
static void listmap(const map<string,string>& mp)
|
|
|
37 |
{
|
|
|
38 |
for (map<string,string>::const_iterator it = mp.begin();
|
|
|
39 |
it != mp.end(); it++) {
|
|
|
40 |
cerr << "[" << it->first << "]->[" << it->second << "] ";
|
|
|
41 |
}
|
|
|
42 |
}
|
|
|
43 |
#endif
|
|
|
44 |
|
|
|
45 |
static const char* upnptags[] = {
|
|
|
46 |
"upnp:artist",
|
|
|
47 |
"upnp:album",
|
|
|
48 |
"upnp:genre",
|
|
|
49 |
"upnp:originalTrackNumber",
|
|
|
50 |
"upnp:class",
|
|
|
51 |
};
|
|
|
52 |
static const int nupnptags = sizeof(upnptags) / sizeof(char*);
|
|
|
53 |
|
|
|
54 |
// An XML parser which builds directory contents from DIDL lite input.
|
31 |
// An XML parser which builds directory contents from DIDL-lite input.
|
55 |
class UPnPDirParser : public expatmm::inputRefXMLParser {
|
32 |
class UPnPDirParser : public expatmm::inputRefXMLParser {
|
56 |
public:
|
33 |
public:
|
57 |
UPnPDirParser(UPnPDirContent& dir, const string& input)
|
34 |
UPnPDirParser(UPnPDirContent& dir, const string& input)
|
58 |
: inputRefXMLParser(input), m_dir(dir)
|
35 |
: inputRefXMLParser(input), m_dir(dir)
|
59 |
{
|
36 |
{
|
60 |
m_okitems["object.item.audioItem.musicTrack"] =
|
37 |
m_okitems["object.item.audioItem.musicTrack"] =
|
61 |
UPnPDirObject::audioItem_musicTrack;
|
38 |
UPnPDirObject::audioItem_musicTrack;
|
62 |
m_okitems["object.item.playlistItem"] =
|
39 |
m_okitems["object.item.playlistItem"] =
|
63 |
UPnPDirObject::audioItem_playlist;
|
40 |
UPnPDirObject::audioItem_playlist;
|
64 |
}
|
41 |
}
|
65 |
UPnPDirContent& m_dir;
|
42 |
UPnPDirContent& m_dir;
|
66 |
|
43 |
|
67 |
protected:
|
44 |
protected:
|
68 |
class StackEl {
|
45 |
class StackEl {
|
69 |
public:
|
46 |
public:
|
70 |
StackEl(const string& nm) : name(nm) {}
|
47 |
StackEl(const string& nm) : name(nm) {}
|
71 |
string name;
|
48 |
string name;
|
72 |
map<string,string> attributes;
|
49 |
map<string,string> attributes;
|
73 |
};
|
50 |
string data;
|
|
|
51 |
};
|
74 |
|
52 |
|
75 |
virtual void StartElement(const XML_Char *name, const XML_Char **attrs)
|
53 |
virtual void StartElement(const XML_Char *name, const XML_Char **attrs)
|
76 |
{
|
54 |
{
|
77 |
//cerr << "startElement: name [" << name << "]" << endl;
|
55 |
//LOGDEB("startElement: name [" << name << "]" << endl);
|
78 |
|
56 |
|
79 |
m_path.push_back(StackEl(name));
|
57 |
m_path.push_back(StackEl(name));
|
80 |
for (int i = 0; attrs[i] != 0; i += 2) {
|
58 |
for (int i = 0; attrs[i] != 0; i += 2) {
|
81 |
m_path.back().attributes[attrs[i]] = attrs[i+1];
|
59 |
m_path.back().attributes[attrs[i]] = attrs[i+1];
|
82 |
}
|
60 |
}
|
83 |
|
61 |
|
84 |
switch (name[0]) {
|
62 |
switch (name[0]) {
|
85 |
case 'c':
|
63 |
case 'c':
|
86 |
if (!strcmp(name, "container")) {
|
64 |
if (!strcmp(name, "container")) {
|
87 |
m_tobj.clear();
|
65 |
m_tobj.clear();
|
88 |
m_tobj.m_type = UPnPDirObject::container;
|
66 |
m_tobj.m_type = UPnPDirObject::container;
|
89 |
m_tobj.m_id = m_path.back().attributes["id"];
|
67 |
m_tobj.m_id = m_path.back().attributes["id"];
|
90 |
m_tobj.m_pid = m_path.back().attributes["parentID"];
|
68 |
m_tobj.m_pid = m_path.back().attributes["parentID"];
|
91 |
}
|
69 |
}
|
92 |
break;
|
70 |
break;
|
93 |
case 'i':
|
71 |
case 'i':
|
94 |
if (!strcmp(name, "item")) {
|
72 |
if (!strcmp(name, "item")) {
|
95 |
m_tobj.clear();
|
73 |
m_tobj.clear();
|
96 |
m_tobj.m_type = UPnPDirObject::item;
|
74 |
m_tobj.m_type = UPnPDirObject::item;
|
97 |
m_tobj.m_id = m_path.back().attributes["id"];
|
75 |
m_tobj.m_id = m_path.back().attributes["id"];
|
98 |
m_tobj.m_pid = m_path.back().attributes["parentID"];
|
76 |
m_tobj.m_pid = m_path.back().attributes["parentID"];
|
99 |
}
|
77 |
}
|
100 |
break;
|
78 |
break;
|
101 |
default:
|
79 |
default:
|
102 |
break;
|
80 |
break;
|
103 |
}
|
81 |
}
|
104 |
}
|
82 |
}
|
105 |
|
83 |
|
106 |
virtual bool checkobjok()
|
84 |
virtual bool checkobjok()
|
107 |
{
|
85 |
{
|
108 |
bool ok = !m_tobj.m_id.empty() && !m_tobj.m_pid.empty() &&
|
86 |
bool ok = !m_tobj.m_id.empty() && !m_tobj.m_pid.empty() &&
|
109 |
!m_tobj.m_title.empty();
|
87 |
!m_tobj.m_title.empty();
|
110 |
|
88 |
|
111 |
if (ok && m_tobj.m_type == UPnPDirObject::item) {
|
89 |
if (ok && m_tobj.m_type == UPnPDirObject::item) {
|
112 |
map<string, UPnPDirObject::ItemClass>::const_iterator it;
|
90 |
map<string, UPnPDirObject::ItemClass>::const_iterator it;
|
113 |
it = m_okitems.find(m_tobj.m_props["upnp:class"]);
|
91 |
it = m_okitems.find(m_tobj.m_props["upnp:class"]);
|
114 |
if (it == m_okitems.end()) {
|
92 |
if (it == m_okitems.end()) {
|
115 |
PLOGINF("checkobjok: found object of unknown class: [%s]\n",
|
93 |
LOGINF("checkobjok: found object of unknown class: [" <<
|
116 |
m_tobj.m_props["upnp:class"].c_str());
|
94 |
m_tobj.m_props["upnp:class"] << endl);
|
117 |
ok = false;
|
95 |
ok = false;
|
118 |
} else {
|
96 |
} else {
|
119 |
m_tobj.m_iclass = it->second;
|
97 |
m_tobj.m_iclass = it->second;
|
120 |
}
|
98 |
}
|
121 |
}
|
99 |
}
|
122 |
|
100 |
|
123 |
if (!ok) {
|
101 |
if (!ok) {
|
124 |
PLOGINF("checkobjok: skip: id [%s] pid [%s] clss [%s] tt [%s]\n",
|
102 |
LOGINF("checkobjok:skip: id ["<< m_tobj.m_id<<"] pid ["<<
|
125 |
m_tobj.m_id.c_str(), m_tobj.m_pid.c_str(),
|
103 |
m_tobj.m_pid << "] clss [" << m_tobj.m_props["upnp:class"]
|
126 |
m_tobj.m_props["upnp:class"].c_str(),
|
104 |
<< "] tt [" << m_tobj.m_title << endl);
|
127 |
m_tobj.m_title.c_str());
|
105 |
}
|
128 |
}
|
106 |
return ok;
|
129 |
return ok;
|
107 |
}
|
130 |
}
|
|
|
131 |
virtual void EndElement(const XML_Char *name)
|
|
|
132 |
{
|
|
|
133 |
if (!strcmp(name, "container")) {
|
|
|
134 |
if (checkobjok()) {
|
|
|
135 |
//cerr << "Pushing container: " << m_tobj.m_title << endl;
|
|
|
136 |
m_dir.m_containers.push_back(m_tobj);
|
|
|
137 |
}
|
|
|
138 |
} else if (!strcmp(name, "item")) {
|
|
|
139 |
if (checkobjok()) {
|
|
|
140 |
//cerr << "Pushing item: " << m_tobj.m_title << endl;
|
|
|
141 |
m_dir.m_items.push_back(m_tobj);
|
|
|
142 |
}
|
|
|
143 |
} else if (!strcmp(name, "res")) {
|
|
|
144 |
// <res protocolInfo="http-get:*:audio/mpeg:*" size="5171496"
|
|
|
145 |
// bitrate="24576" duration="00:03:35" sampleFrequency="44100"
|
|
|
146 |
// nrAudioChannels="2">
|
|
|
147 |
string s;
|
|
|
148 |
s="protocolInfo";m_tobj.m_props[s] = m_path.back().attributes[s];
|
|
|
149 |
s="size";m_tobj.m_props[s] = m_path.back().attributes[s];
|
|
|
150 |
s="bitrate";m_tobj.m_props[s] = m_path.back().attributes[s];
|
|
|
151 |
s="duration";m_tobj.m_props[s] = m_path.back().attributes[s];
|
|
|
152 |
s="sampleFrequency";m_tobj.m_props[s] = m_path.back().attributes[s];
|
|
|
153 |
s="nrAudioChannels";m_tobj.m_props[s] = m_path.back().attributes[s];
|
|
|
154 |
}
|
|
|
155 |
|
108 |
|
156 |
m_path.pop_back();
|
109 |
virtual void EndElement(const XML_Char *name)
|
157 |
}
|
110 |
{
|
|
|
111 |
string parentname;
|
|
|
112 |
if (m_path.size() == 1) {
|
|
|
113 |
parentname = "root";
|
|
|
114 |
} else {
|
|
|
115 |
parentname = m_path[m_path.size()-2].name;
|
|
|
116 |
}
|
|
|
117 |
//LOGDEB("Closing element " << name << " inside element " <<
|
|
|
118 |
// parentname << endl);
|
|
|
119 |
if (!strcmp(name, "container")) {
|
|
|
120 |
if (checkobjok()) {
|
|
|
121 |
m_dir.m_containers.push_back(m_tobj);
|
|
|
122 |
}
|
|
|
123 |
} else if (!strcmp(name, "item")) {
|
|
|
124 |
if (checkobjok()) {
|
|
|
125 |
m_dir.m_items.push_back(m_tobj);
|
|
|
126 |
}
|
|
|
127 |
} else if (!parentname.compare("item")) {
|
|
|
128 |
switch (name[0]) {
|
|
|
129 |
case 'd':
|
|
|
130 |
if (!strcmp(name, "dc:title"))
|
|
|
131 |
m_tobj.m_title = m_path.back().data;
|
|
|
132 |
break;
|
|
|
133 |
case 'r':
|
|
|
134 |
if (!strcmp(name, "res")) {
|
|
|
135 |
// <res protocolInfo="http-get:*:audio/mpeg:*" size="517149"
|
|
|
136 |
// bitrate="24576" duration="00:03:35"
|
|
|
137 |
// sampleFrequency="44100" nrAudioChannels="2">
|
|
|
138 |
UPnPResource res;
|
|
|
139 |
res.m_uri = m_path.back().data;
|
|
|
140 |
for (auto& entry: m_path.back().attributes) {
|
|
|
141 |
res.m_props[entry.first] = entry.second;
|
|
|
142 |
}
|
|
|
143 |
m_tobj.m_resources.push_back(res);
|
|
|
144 |
}
|
|
|
145 |
break;
|
|
|
146 |
default:
|
|
|
147 |
m_tobj.m_props[name] = m_path.back().data;
|
|
|
148 |
break;
|
|
|
149 |
}
|
158 |
|
150 |
|
|
|
151 |
}
|
|
|
152 |
|
|
|
153 |
m_path.pop_back();
|
|
|
154 |
}
|
|
|
155 |
|
159 |
virtual void CharacterData(const XML_Char *s, int len)
|
156 |
virtual void CharacterData(const XML_Char *s, int len)
|
160 |
{
|
157 |
{
|
161 |
if (s == 0 || *s == 0)
|
158 |
if (s == 0 || *s == 0)
|
162 |
return;
|
159 |
return;
|
163 |
string str(s, len);
|
160 |
string str(s, len);
|
164 |
trimstring(str);
|
161 |
trimstring(str);
|
165 |
switch (m_path.back().name[0]) {
|
162 |
m_path.back().data += str;
|
166 |
case 'd':
|
163 |
}
|
167 |
if (!m_path.back().name.compare("dc:title"))
|
|
|
168 |
m_tobj.m_title += str;
|
|
|
169 |
break;
|
|
|
170 |
case 'r':
|
|
|
171 |
if (!m_path.back().name.compare("res")) {
|
|
|
172 |
m_tobj.m_props["url"] += str;
|
|
|
173 |
}
|
|
|
174 |
break;
|
|
|
175 |
case 'u':
|
|
|
176 |
for (int i = 0; i < nupnptags; i++) {
|
|
|
177 |
if (!m_path.back().name.compare(upnptags[i])) {
|
|
|
178 |
m_tobj.m_props[upnptags[i]] += str;
|
|
|
179 |
}
|
|
|
180 |
}
|
|
|
181 |
break;
|
|
|
182 |
}
|
|
|
183 |
}
|
|
|
184 |
|
164 |
|
185 |
private:
|
165 |
private:
|
186 |
vector<StackEl> m_path;
|
166 |
vector<StackEl> m_path;
|
187 |
UPnPDirObject m_tobj;
|
167 |
UPnPDirObject m_tobj;
|
188 |
map<string, UPnPDirObject::ItemClass> m_okitems;
|
168 |
map<string, UPnPDirObject::ItemClass> m_okitems;
|
189 |
};
|
169 |
};
|
190 |
|
170 |
|
191 |
bool UPnPDirContent::parse(const std::string& input)
|
171 |
bool UPnPDirContent::parse(const std::string& input)
|
192 |
{
|
172 |
{
|
193 |
UPnPDirParser parser(*this, input);
|
173 |
UPnPDirParser parser(*this, input);
|
194 |
return parser.Parse();
|
174 |
return parser.Parse();
|
195 |
}
|
175 |
}
|
196 |
/* Local Variables: */
|
|
|
197 |
/* mode: c++ */
|
|
|
198 |
/* c-basic-offset: 4 */
|
|
|
199 |
/* tab-width: 4 */
|
|
|
200 |
/* indent-tabs-mode: t */
|
|
|
201 |
/* End: */
|
|
|