|
a/src/cdplugins/plgwithslave.cxx |
|
b/src/cdplugins/plgwithslave.cxx |
|
... |
|
... |
245 |
// RTMP, apparently because of the use of a different API key. Look up
|
245 |
// RTMP, apparently because of the use of a different API key. Look up
|
246 |
// the git history if you need this again.
|
246 |
// the git history if you need this again.
|
247 |
// The Python code calls the service to translate the trackid to a temp
|
247 |
// The Python code calls the service to translate the trackid to a temp
|
248 |
// URL. We cache the result for a few seconds to avoid multiple calls
|
248 |
// URL. We cache the result for a few seconds to avoid multiple calls
|
249 |
// to tidal.
|
249 |
// to tidal.
|
250 |
string PlgWithSlave::get_media_url(const std::string& path)
|
250 |
string PlgWithSlave::get_media_url(const string& path)
|
251 |
{
|
251 |
{
|
252 |
LOGDEB("PlgWithSlave::get_media_url: " << path << endl);
|
252 |
LOGDEB("PlgWithSlave::get_media_url: " << path << endl);
|
253 |
if (!m->maybeStartCmd()) {
|
253 |
if (!m->maybeStartCmd()) {
|
254 |
return string();
|
254 |
return string();
|
255 |
}
|
255 |
}
|
|
... |
|
... |
276 |
LOGDEB("PlgWithSlave: got media url [" << m->laststream.media_url << "]\n");
|
276 |
LOGDEB("PlgWithSlave: got media url [" << m->laststream.media_url << "]\n");
|
277 |
return m->laststream.media_url;
|
277 |
return m->laststream.media_url;
|
278 |
}
|
278 |
}
|
279 |
|
279 |
|
280 |
|
280 |
|
281 |
PlgWithSlave::PlgWithSlave(const std::string& name, CDPluginServices *services)
|
281 |
PlgWithSlave::PlgWithSlave(const string& name, CDPluginServices *services)
|
282 |
: CDPlugin(name, services)
|
282 |
: CDPlugin(name, services)
|
283 |
{
|
283 |
{
|
284 |
m = new Internal(this, services->getexecpath(this),
|
284 |
m = new Internal(this, services->getexecpath(this),
|
285 |
services->getupnpaddr(this),
|
285 |
services->getupnpaddr(this),
|
286 |
services->getupnpport(this),
|
286 |
services->getupnpport(this),
|
|
... |
|
... |
291 |
{
|
291 |
{
|
292 |
delete m;
|
292 |
delete m;
|
293 |
}
|
293 |
}
|
294 |
|
294 |
|
295 |
static int resultToEntries(const string& encoded, int stidx, int cnt,
|
295 |
static int resultToEntries(const string& encoded, int stidx, int cnt,
|
296 |
std::vector<UpSong>& entries)
|
296 |
vector<UpSong>& entries)
|
297 |
{
|
297 |
{
|
|
|
298 |
entries.clear();
|
298 |
auto decoded = json::parse(encoded);
|
299 |
auto decoded = json::parse(encoded);
|
299 |
LOGDEB("PlgWithSlave::results: got " << decoded.size() << " entries\n");
|
300 |
LOGDEB0("PlgWithSlave::results: got " << decoded.size() << " entries\n");
|
300 |
LOGDEB1("PlgWithSlave::results: undecoded json: " << decoded.dump() << endl);
|
301 |
LOGDEB1("PlgWithSlave::results: undecoded: " << decoded.dump() << endl);
|
301 |
|
302 |
|
302 |
for (unsigned int i = stidx; i < decoded.size(); i++) {
|
303 |
for (unsigned int i = stidx; i < decoded.size(); i++) {
|
303 |
if (--cnt < 0) {
|
304 |
if (--cnt < 0) {
|
304 |
break;
|
305 |
break;
|
305 |
}
|
306 |
}
|
|
... |
|
... |
329 |
JSONTOUPS(genre, upnp:genre);
|
330 |
JSONTOUPS(genre, upnp:genre);
|
330 |
JSONTOUPS(tracknum, upnp:originalTrackNumber);
|
331 |
JSONTOUPS(tracknum, upnp:originalTrackNumber);
|
331 |
JSONTOUPS(artUri, upnp:albumArtURI);
|
332 |
JSONTOUPS(artUri, upnp:albumArtURI);
|
332 |
JSONTOUPS(duration_secs, duration);
|
333 |
JSONTOUPS(duration_secs, duration);
|
333 |
} else {
|
334 |
} else {
|
334 |
LOGERR("PlgWithSlave::browse: bad type in entry: " << it1.value() << endl);
|
335 |
LOGERR("PlgWithSlave::browse: bad type in entry: " <<
|
|
|
336 |
it1.value() << endl);
|
335 |
continue;
|
337 |
continue;
|
336 |
}
|
338 |
}
|
337 |
JSONTOUPS(id, id);
|
339 |
JSONTOUPS(id, id);
|
338 |
JSONTOUPS(parentid, pid);
|
340 |
JSONTOUPS(parentid, pid);
|
339 |
JSONTOUPS(title, tt);
|
341 |
JSONTOUPS(title, tt);
|
|
... |
|
... |
342 |
// We return the total match size, the count of actually returned
|
344 |
// We return the total match size, the count of actually returned
|
343 |
// entries can be obtained from the vector
|
345 |
// entries can be obtained from the vector
|
344 |
return decoded.size();
|
346 |
return decoded.size();
|
345 |
}
|
347 |
}
|
346 |
|
348 |
|
|
|
349 |
// Better return a bogus informative entry than an outright error:
|
|
|
350 |
static int errorEntries(const string& pid, vector<UpSong>& entries)
|
|
|
351 |
{
|
|
|
352 |
entries.clear();
|
|
|
353 |
entries.push_back(
|
|
|
354 |
UpSong::item(pid + "$bogus", pid,
|
|
|
355 |
"Service login or communication failure"));
|
|
|
356 |
return 1;
|
|
|
357 |
}
|
|
|
358 |
|
347 |
int PlgWithSlave::browse(const std::string& objid, int stidx, int cnt,
|
359 |
int PlgWithSlave::browse(const string& objid, int stidx, int cnt,
|
348 |
std::vector<UpSong>& entries,
|
360 |
vector<UpSong>& entries,
|
349 |
const std::vector<std::string>& sortcrits,
|
361 |
const vector<string>& sortcrits,
|
350 |
BrowseFlag flg)
|
362 |
BrowseFlag flg)
|
351 |
{
|
363 |
{
|
352 |
LOGDEB("PlgWithSlave::browse\n");
|
364 |
LOGDEB("PlgWithSlave::browse\n");
|
353 |
if (!m->maybeStartCmd()) {
|
365 |
if (!m->maybeStartCmd()) {
|
354 |
return -1;
|
366 |
return errorEntries(objid, entries);
|
355 |
}
|
367 |
}
|
356 |
string sbflg;
|
368 |
string sbflg;
|
357 |
switch (flg) {
|
369 |
switch (flg) {
|
358 |
case CDPlugin::BFMeta:
|
370 |
case CDPlugin::BFMeta:
|
359 |
sbflg = "meta";
|
371 |
sbflg = "meta";
|
|
... |
|
... |
365 |
}
|
377 |
}
|
366 |
|
378 |
|
367 |
unordered_map<string, string> res;
|
379 |
unordered_map<string, string> res;
|
368 |
if (!m->cmd.callproc("browse", {{"objid", objid}, {"flag", sbflg}}, res)) {
|
380 |
if (!m->cmd.callproc("browse", {{"objid", objid}, {"flag", sbflg}}, res)) {
|
369 |
LOGERR("PlgWithSlave::browse: slave failure\n");
|
381 |
LOGERR("PlgWithSlave::browse: slave failure\n");
|
370 |
return -1;
|
382 |
return errorEntries(objid, entries);
|
371 |
}
|
383 |
}
|
372 |
|
384 |
|
373 |
auto it = res.find("entries");
|
385 |
auto it = res.find("entries");
|
374 |
if (it == res.end()) {
|
386 |
if (it == res.end()) {
|
375 |
LOGERR("PlgWithSlave::browse: no entries returned\n");
|
387 |
LOGERR("PlgWithSlave::browse: no entries returned\n");
|
376 |
return -1;
|
388 |
return errorEntries(objid, entries);
|
377 |
}
|
389 |
}
|
378 |
return resultToEntries(it->second, stidx, cnt, entries);
|
390 |
return resultToEntries(it->second, stidx, cnt, entries);
|
379 |
}
|
391 |
}
|
380 |
|
392 |
|
381 |
|
393 |
|
|
... |
|
... |
384 |
vector<UpSong>& entries,
|
396 |
vector<UpSong>& entries,
|
385 |
const vector<string>& sortcrits)
|
397 |
const vector<string>& sortcrits)
|
386 |
{
|
398 |
{
|
387 |
LOGDEB("PlgWithSlave::search\n");
|
399 |
LOGDEB("PlgWithSlave::search\n");
|
388 |
if (!m->maybeStartCmd()) {
|
400 |
if (!m->maybeStartCmd()) {
|
389 |
return -1;
|
401 |
return errorEntries(ctid, entries);
|
390 |
}
|
402 |
}
|
391 |
|
403 |
|
392 |
// We only accept field xx value as search criteria
|
404 |
// We only accept field xx value as search criteria
|
393 |
vector<string> vs;
|
405 |
vector<string> vs;
|
394 |
stringToStrings(searchstr, vs);
|
406 |
stringToStrings(searchstr, vs);
|
395 |
if (vs.size() != 3) {
|
407 |
if (vs.size() != 3) {
|
396 |
LOGERR("PlgWithSlave::search: bad search string: [" << searchstr <<
|
408 |
LOGERR("PlgWithSlave::search: bad search string: [" << searchstr <<
|
397 |
"]\n");
|
409 |
"]\n");
|
398 |
return -1;
|
410 |
return errorEntries(ctid, entries);
|
399 |
}
|
411 |
}
|
400 |
const string& upnpproperty = vs[0];
|
412 |
const string& upnpproperty = vs[0];
|
401 |
string slavefield;
|
413 |
string slavefield;
|
402 |
if (!upnpproperty.compare("upnp:artist") ||
|
414 |
if (!upnpproperty.compare("upnp:artist") ||
|
403 |
!upnpproperty.compare("dc:author")) {
|
415 |
!upnpproperty.compare("dc:author")) {
|
|
... |
|
... |
405 |
} else if (!upnpproperty.compare("upnp:album")) {
|
417 |
} else if (!upnpproperty.compare("upnp:album")) {
|
406 |
slavefield = "album";
|
418 |
slavefield = "album";
|
407 |
} else if (!upnpproperty.compare("dc:title")) {
|
419 |
} else if (!upnpproperty.compare("dc:title")) {
|
408 |
slavefield = "track";
|
420 |
slavefield = "track";
|
409 |
} else {
|
421 |
} else {
|
410 |
LOGERR("PlgWithSlave::search: bad property: [" << upnpproperty << "]\n");
|
422 |
LOGERR("PlgWithSlave::search: bad property: [" << upnpproperty << endl);
|
411 |
return -1;
|
423 |
return errorEntries(ctid, entries);
|
412 |
}
|
424 |
}
|
413 |
|
425 |
|
414 |
unordered_map<string, string> res;
|
426 |
unordered_map<string, string> res;
|
415 |
if (!m->cmd.callproc("search", {
|
427 |
if (!m->cmd.callproc("search", {
|
416 |
{"objid", ctid},
|
428 |
{"ctid", ctid},
|
417 |
{"field", slavefield},
|
429 |
{"field", slavefield},
|
418 |
{"value", vs[2]} }, res)) {
|
430 |
{"value", vs[2]} }, res)) {
|
419 |
LOGERR("PlgWithSlave::search: slave failure\n");
|
431 |
LOGERR("PlgWithSlave::search: slave failure\n");
|
420 |
return -1;
|
432 |
return errorEntries(ctid, entries);
|
421 |
}
|
433 |
}
|
422 |
|
434 |
|
423 |
auto it = res.find("entries");
|
435 |
auto it = res.find("entries");
|
424 |
if (it == res.end()) {
|
436 |
if (it == res.end()) {
|
425 |
LOGERR("PlgWithSlave::search: no entries returned\n");
|
437 |
LOGERR("PlgWithSlave::search: no entries returned\n");
|
426 |
return -1;
|
438 |
return errorEntries(ctid, entries);
|
427 |
}
|
439 |
}
|
428 |
return resultToEntries(it->second, stidx, cnt, entries);
|
440 |
return resultToEntries(it->second, stidx, cnt, entries);
|
429 |
}
|
441 |
}
|