metadata.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. #ifdef _WIN32
  2. #include <winsock2.h>
  3. #endif
  4. #include "metadata.h"
  5. #include "aolxml/aolxml.h"
  6. #include "filenameMetadata.h"
  7. #include "file/fileUtils.h"
  8. #include "stl/stringUtils.h"
  9. #include "services/stdServiceImpl.h"
  10. #include "streamData.h"
  11. using namespace std;
  12. using namespace stringUtil;
  13. using namespace uniString;
  14. metadata::metadata(const metadata &m)
  15. {
  16. copy(m);
  17. }
  18. metadata& metadata::operator=(const metadata &m)
  19. {
  20. copy(m);
  21. return *this;
  22. }
  23. void metadata::clear() throw()
  24. {
  25. for (keyValueMap_t::const_iterator i = m_keyValueMap.begin(); i != m_keyValueMap.end(); ++i)
  26. {
  27. delete (*i).second;
  28. }
  29. m_keyValueMap.clear();
  30. }
  31. void metadata::copy(const metadata &m) throw()
  32. {
  33. clear();
  34. for (keyValueMap_t::const_iterator i = m.m_keyValueMap.begin(); i != m.m_keyValueMap.end(); ++i)
  35. {
  36. if ((*i).second)
  37. {
  38. m_keyValueMap.insert(make_pair((*i).first,(*i).second->clone()));
  39. }
  40. }
  41. }
  42. metadata::~metadata() throw()
  43. {
  44. clear();
  45. }
  46. // remove all values for key
  47. void metadata::removeValue(const string &key) throw()
  48. {
  49. keyValueMap_t::iterator i = m_keyValueMap.find(key);
  50. while (i != m_keyValueMap.end())
  51. {
  52. delete (*i).second;
  53. m_keyValueMap.erase(i);
  54. i = m_keyValueMap.find(key);
  55. }
  56. }
  57. bool metadata::valueExists(const string &key) const throw()
  58. {
  59. return (m_keyValueMap.find(key) != m_keyValueMap.end());
  60. }
  61. //// only returns first one found
  62. const metadata::metaValue_base* metadata::getValue(const string &key) const throw()
  63. {
  64. keyValueMap_t::const_iterator i = m_keyValueMap.find(key);
  65. return (i == m_keyValueMap.end() ? 0 : (*i).second);
  66. }
  67. //// only returns first one found
  68. utf8 metadata::getValueAsString(const std::string &key) const throw()
  69. {
  70. static utf8 empty;
  71. const metaValue_base *mv = getValue(key);
  72. return (mv ? mv->toString() : empty);
  73. }
  74. void metadata::setValue(const string &key,metadata::metaValue_base *value) throw()
  75. {
  76. m_keyValueMap.insert(make_pair(key,value));
  77. }
  78. utf8 metadata::safeXML(const string &tag,const metaValue_base *m) throw()
  79. {
  80. static utf8 empty;
  81. if (!m) return empty;
  82. return m->toXML(tag);
  83. }
  84. utf8 metadata::safeString(const metaValue_base *m) throw()
  85. {
  86. static utf8 empty;
  87. if (!m) return empty;
  88. return m->toString();
  89. }
  90. bool metadata::noMeaningfulMetadata() const throw()
  91. {
  92. return (m_keyValueMap.find(NAME()) == m_keyValueMap.end());
  93. }
  94. bool metadata::get_replayGain(double &gain) const throw()
  95. {
  96. pair<keyValueMap_t::const_iterator,keyValueMap_t::const_iterator> mdrange = m_keyValueMap.equal_range("TXXX");
  97. for (keyValueMap_t::const_iterator i = mdrange.first; i != mdrange.second; ++i)
  98. {
  99. metadata::metaValue<ID3V2::userText_t> *md = dynamic_cast<metadata::metaValue<ID3V2::userText_t> *>((*i).second);
  100. if (md)
  101. {
  102. ID3V2::userText_t ut = md->value();
  103. if (ut.m_id == "replaygain_track_gain" || ut.m_id == "REPLAYGAIN_TRACK_GAIN")
  104. {
  105. gain = atof((const char *)ut.m_text.c_str());
  106. return true;
  107. }
  108. }
  109. }
  110. return false;
  111. }
  112. bool metadata::get_replayGain() const throw()
  113. {
  114. double g;
  115. return get_replayGain(g);
  116. }
  117. const utf8 METADATA("<metadata>");
  118. const utf8 E_METADATA("</metadata>");
  119. #ifdef XML_DEBUG
  120. static const utf8 EOL(eol());
  121. #else
  122. static const utf8 EOL("");
  123. #endif
  124. // new xml for shoutcast
  125. utf8 metadata::toXML() const throw()
  126. {
  127. utf8 o;
  128. o += "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + EOL;
  129. o += METADATA + EOL;
  130. for (keyValueMap_t::const_iterator i = m_keyValueMap.begin(); i != m_keyValueMap.end(); ++i)
  131. {
  132. o += safeXML((*i).first,(*i).second) + EOL;
  133. }
  134. o += E_METADATA + EOL;
  135. return o;
  136. }
  137. utf8 metadata::toXML_fromFilename(const uniFile::filenameType &filename,const uniFile::filenameType &url,const utf8 &pattern) throw()
  138. {
  139. utf8 o;
  140. o += "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + EOL;
  141. o += METADATA + EOL;
  142. bool patternWorked(false);
  143. try
  144. {
  145. filenameMetadata fm;
  146. fm.setPattern(pattern);
  147. fm.parse(filename);
  148. const std::map<uniString::utf8,uniString::utf8>&m = fm.getTokens();
  149. for (map<utf8,utf8>::const_iterator i = m.begin(); i != m.end(); ++i)
  150. {
  151. o += "<" + (*i).first + ">" + (*i).second.escapeXML() + "</" + (*i).first + ">" + EOL;
  152. }
  153. patternWorked = true;
  154. }
  155. catch(.../*const exception &ex*/)
  156. {
  157. }
  158. if (!patternWorked)
  159. {
  160. try
  161. {
  162. string str = string(filename.toANSI(true));
  163. utf8 song = asciiToUtf8(str);
  164. filenameMetadata fm;
  165. fm.setPattern(pattern);
  166. fm.parse(song);
  167. const std::map<uniString::utf8,uniString::utf8>&m = fm.getTokens();
  168. for (map<utf8,utf8>::const_iterator i = m.begin(); i != m.end(); ++i)
  169. {
  170. o += "<" + (*i).first + ">" + (*i).second.escapeXML() + "</" + (*i).first + ">" + EOL;
  171. }
  172. patternWorked = true;
  173. }
  174. catch(.../*const exception &ex*/)
  175. {
  176. //ELOG(string("[METADATA] Failure converting filename to metadata ") + ex.what());
  177. }
  178. }
  179. if (!patternWorked)
  180. {
  181. o += "<TIT2>";
  182. utf8 us = fileUtil::stripSuffix(filename);
  183. // remove path based on delimiter for Unix (/) Win32 (\) or MacOS (:)
  184. us = fileUtil::stripPath(us,utf8("/"));
  185. us = fileUtil::stripPath(us,utf8("\\"));
  186. us = fileUtil::stripPath(us,utf8(":"));
  187. o += us.escapeXML();
  188. o += "</TIT2>" + EOL;
  189. }
  190. if (!url.empty())
  191. {
  192. utf8 u = url;
  193. if ((u.find(utf8("://")) == utf8::npos) &&
  194. (u.find(utf8("&")) != 0) &&
  195. u.find(utf8("DNAS/streamart?sid=")) == utf8::npos &&
  196. u.find(utf8("DNAS/playingart?sid=")) == utf8::npos)
  197. {
  198. u = "http://" + u;
  199. }
  200. o += "<URL>" + u.escapeXML() + "</URL>" + EOL;
  201. }
  202. o += E_METADATA + EOL;
  203. return o;
  204. }
  205. utf8 metadata::toFixedString(const uniFile::filenameType &filename) throw()
  206. {
  207. utf8 o;
  208. bool patternWorked(false);
  209. try
  210. {
  211. filenameMetadata fm;
  212. fm.setPattern("%N");
  213. fm.parse(filename);
  214. const std::map<uniString::utf8,uniString::utf8>&m = fm.getTokens();
  215. for (map<utf8,utf8>::const_iterator i = m.begin(); i != m.end(); ++i)
  216. {
  217. o = (*i).second;
  218. }
  219. patternWorked = true;
  220. }
  221. catch(.../*const exception &ex*/)
  222. {
  223. }
  224. if (!patternWorked)
  225. {
  226. try
  227. {
  228. string str = string(filename.toANSI(true));
  229. utf8 song = asciiToUtf8(str);
  230. filenameMetadata fm;
  231. fm.setPattern("%N");
  232. fm.parse(song);
  233. const std::map<uniString::utf8,uniString::utf8>&m = fm.getTokens();
  234. for (map<utf8,utf8>::const_iterator i = m.begin(); i != m.end(); ++i)
  235. {
  236. o = (*i).second;
  237. }
  238. patternWorked = true;
  239. }
  240. catch(.../*const exception &ex*/)
  241. {
  242. //ELOG(string("[METADATA] Failure converting filename to metadata ") + ex.what());
  243. }
  244. }
  245. if (!patternWorked)
  246. {
  247. o = filename;
  248. }
  249. return o;
  250. }
  251. uniString::utf8 metadata::convert_3902_to_shoutcast1(const uniString::utf8 &d, const streamID_t id) throw (std::runtime_error)
  252. {
  253. utf8 o;
  254. aolxml::node *n = 0;
  255. try
  256. {
  257. n = aolxml::node::parse(d.hideAsString());
  258. utf8 artist = aolxml::subNodeText(n, "/metadata/" + ARTIST(), (utf8)"");
  259. utf8 name = aolxml::subNodeText(n, "/metadata/" + NAME(), (utf8)"");
  260. if (!artist.empty())
  261. {
  262. if (!o.empty())
  263. {
  264. o += " - ";
  265. }
  266. o += artist;
  267. }
  268. if (!name.empty())
  269. {
  270. if (!o.empty())
  271. {
  272. o += " - ";
  273. }
  274. o += name;
  275. }
  276. if (!streamData::validateTitle(o))
  277. {
  278. WLOG("[ADMINCGI sid=" + tos(id) + "] Title update rejected - value not allowed: " + o);
  279. o.clear();
  280. }
  281. o = "StreamTitle='" + o + "';";
  282. aolxml::node::nodeList_t nodes = aolxml::node::findNodes(n,"/metadata/extension/title");
  283. if (!nodes.empty())
  284. {
  285. for (aolxml::node::nodeList_t::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
  286. {
  287. if (*i)
  288. {
  289. // skip the first element as that is the current song and not what we want to get
  290. int seq = atoi((*i)->findAttributeString("seq").c_str());
  291. if (seq == 2)
  292. {
  293. utf8 next = ((*i)->pcdata());
  294. if (streamData::validateTitle(next))
  295. {
  296. o += "StreamNext='" + next + "';";
  297. }
  298. break;
  299. }
  300. }
  301. }
  302. }
  303. utf8 url = aolxml::subNodeText(n, "/metadata/" + URL(), (utf8)"");
  304. if (!url.empty())
  305. {
  306. utf8::size_type pos = url.find(utf8("://"));
  307. if (pos == utf8::npos)
  308. {
  309. url = "http://" + url;
  310. }
  311. o += "StreamUrl='" + url + "';";
  312. }
  313. }
  314. catch(const exception &ex)
  315. {
  316. forget(n);
  317. throw std::runtime_error(ex.what());
  318. }
  319. forget(n);
  320. return o;
  321. }
  322. uniString::utf8 metadata::get_song_title_from_3902(const uniString::utf8 &d)
  323. {
  324. utf8 o;
  325. utf8 result;
  326. aolxml::node *n = 0;
  327. try
  328. {
  329. n = aolxml::node::parse(d.hideAsString());
  330. utf8 artist = aolxml::subNodeText(n, "/metadata/" + ARTIST(), (utf8)"");
  331. utf8 name = aolxml::subNodeText(n, "/metadata/" + NAME(), (utf8)"");
  332. if (!artist.empty())
  333. {
  334. if (!result.empty()) result += " - ";
  335. result += artist;
  336. }
  337. if (!name.empty())
  338. {
  339. if (!result.empty()) result += " - ";
  340. result += name;
  341. }
  342. }
  343. catch(const exception &ex)
  344. {
  345. forget(n);
  346. throw std::runtime_error(ex.what());
  347. }
  348. forget(n);
  349. return result;
  350. }
  351. uniString::utf8 metadata::get_XX_from_3902(const uniString::utf8& node, const uniString::utf8 &d,
  352. const uniString::utf8 &old) throw (std::runtime_error)
  353. {
  354. utf8 result;
  355. aolxml::node *n = 0;
  356. try
  357. {
  358. n = aolxml::node::parse(d.hideAsString());
  359. result = aolxml::subNodeText(n, utf8("/metadata/" + node).hideAsString(), old);
  360. }
  361. catch(const exception &ex)
  362. {
  363. forget(n);
  364. throw std::runtime_error(ex.what());
  365. }
  366. forget(n);
  367. return result;
  368. }
  369. std::vector<uniString::utf8> metadata::get_nextsongs_from_3902(const uniString::utf8 &d,
  370. std::vector<uniString::utf8>& oldSongList,
  371. const bool first) throw (std::runtime_error)
  372. {
  373. aolxml::node *n = 0;
  374. std::vector<uniString::utf8> nextSongList;
  375. try
  376. {
  377. n = aolxml::node::parse(d.hideAsString());
  378. aolxml::node::nodeList_t nodes = aolxml::node::findNodes(n,"/metadata/extension/title");
  379. if(!nodes.empty())
  380. {
  381. for (aolxml::node::nodeList_t::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
  382. {
  383. if (*i)
  384. {
  385. // skip the first element as that is the current song and not what we want to hold
  386. int seq = atoi((*i)->findAttributeString("seq").c_str());
  387. if ((seq > 1 && !first) || (first && seq == 2))
  388. {
  389. nextSongList.push_back((*i)->pcdata());
  390. }
  391. if (first)
  392. {
  393. break;
  394. }
  395. }
  396. }
  397. }
  398. else
  399. {
  400. // if there are no nodes then as this could be from a stream-specific metadata
  401. // update then we need to preserve the existing list so it's not cleared out.
  402. forget(n);
  403. return oldSongList;
  404. }
  405. }
  406. catch(const exception &ex)
  407. {
  408. forget(n);
  409. throw std::runtime_error(ex.what());
  410. }
  411. forget(n);
  412. return nextSongList;
  413. }