a b/src/cdplugins/tidal.cxx
1
/* Copyright (C) 2016 J.F.Dockes
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
17
18
#include "tidal.hxx"
19
20
#include <string>
21
#include <vector>
22
23
#include "cmdtalk.h"
24
25
#include "pathut.h"
26
#include "log.hxx"
27
#include "json.hpp"
28
29
using namespace std;
30
extern string g_datadir, g_configfilename;
31
32
using json = nlohmann::json;
33
34
class Tidal::Internal {
35
public:
36
    Internal(const vector<string>& pth)
37
  : path(pth) {
38
    }
39
    CmdTalk cmd;
40
    bool maybeStartCmd() {
41
  LOGDEB("Tidal::maybeStartCmd\n");
42
  if (!cmd.running()) {
43
      string pythonpath = string("PYTHONPATH=") +
44
      path_cat(g_datadir, "cdplugins/pycommon");
45
      string configname = string("UPMPD_CONFIG=") +
46
      g_configfilename;
47
      LOGDEB("Tidal::maybeStartCmd: calling startCmd\n");
48
      if (!cmd.startCmd("tidal.py", {/*args*/},
49
                {pythonpath, configname}, path)) {
50
      LOGDEB("Tidal::maybeStartCmd: startCmd failed\n");
51
      return false;
52
      }
53
      LOGDEB("Tidal::maybeStartCmd: startCmd ok\n");
54
  }
55
  LOGDEB("Tidal::maybeStartCmd: cmd running\n");
56
  return true;
57
    }
58
59
    vector<string> path;
60
};
61
62
Tidal::Tidal(const vector<string>& plgpath)
63
    : m(new Internal(plgpath))
64
{
65
}
66
67
Tidal::~Tidal()
68
{
69
    delete m;
70
}
71
72
int Tidal::browse(const std::string& objid, int stidx, int cnt,
73
        std::vector<UpSong>& entries,
74
        const std::vector<std::string>& sortcrits,
75
        BrowseFlag flg)
76
{
77
    LOGDEB("Tidal::browse\n");
78
    if (!m->maybeStartCmd()) {
79
  LOGERR("Tidal::browse: startcmd failed\n");
80
  return -1;
81
    }
82
    unordered_map<string, string> res;
83
    if (!m->cmd.callproc("browse", {{"objid", objid}}, res)) {
84
  LOGERR("Tidal::browse: slave failure\n");
85
  return -1;
86
    }
87
88
    auto it = res.find("entries");
89
    if (it == res.end()) {
90
  LOGERR("Tidal::browse: no entries returned\n");
91
  return -1;
92
    }
93
94
    auto decoded = json::parse(it->second);
95
    LOGDEB("Tidal::browse: got " << decoded.size() << " entries\n");
96
    LOGDEB1("Tidal::browse: undecoded json: " << decoded.dump() << endl);
97
    
98
    for (const auto& it : decoded) {
99
  UpSong song;
100
  auto it1 = it.find("tp");
101
  if (it1 == it.end()) {
102
      LOGERR("Tidal::browse: no type in entry\n");
103
      continue;
104
  }
105
  string stp = it1.value();
106
  
107
#define JSONTOUPS(fld, nm)                        \
108
  it1 = it.find(#nm);                     \
109
  if (it1 != it.end()) {                      \
110
      /*LOGDEB("song." #fld " = " << it1.value() << endl);*/  \
111
      song.fld = it1.value();                 \
112
  }
113
  
114
  if (!stp.compare("ct")) {
115
      song.iscontainer = true;
116
  } else  if (!stp.compare("it")) {
117
      song.iscontainer = false;
118
      JSONTOUPS(uri, uri);
119
      JSONTOUPS(artist, dc:creator);
120
      JSONTOUPS(artist, upnp:artist);
121
      JSONTOUPS(genre, upnp:genre);
122
      JSONTOUPS(tracknum, upnp:originalTrackNumber);
123
      JSONTOUPS(artUri, upnp:albumArtURI);
124
      JSONTOUPS(duration_secs, duration);
125
  } else {
126
      LOGERR("Tidal::browse: bad type in entry: " << it1.value() << endl);
127
      continue;
128
  }
129
  JSONTOUPS(id, id);
130
  JSONTOUPS(parentid, pid);
131
  JSONTOUPS(title, tt);
132
  entries.push_back(song);
133
    }
134
    return decoded.size();
135
}