|
a/src/cdplugins/tidal.cxx |
|
b/src/cdplugins/tidal.cxx |
|
... |
|
... |
39 |
using namespace UPnPProvider;
|
39 |
using namespace UPnPProvider;
|
40 |
|
40 |
|
41 |
class Tidal::Internal {
|
41 |
class Tidal::Internal {
|
42 |
public:
|
42 |
public:
|
43 |
Internal(const vector<string>& pth, const string& hp, const string& pp)
|
43 |
Internal(const vector<string>& pth, const string& hp, const string& pp)
|
44 |
: path(pth), httphp(hp), pathprefix(pp) { }
|
44 |
: path(pth), httphp(hp), pathprefix(pp), lasttime(0) { }
|
45 |
|
45 |
|
46 |
bool maybeStartCmd(const string&);
|
46 |
bool maybeStartCmd(const string&);
|
47 |
string get_media_url(const std::string& path);
|
47 |
string get_media_url(const std::string& path);
|
48 |
string get_mimetype(const std::string& path);
|
48 |
string get_mimetype(const std::string& path);
|
49 |
|
49 |
|
|
... |
|
... |
53 |
off_t seek(void *hdl, off_t offs, int whence);
|
53 |
off_t seek(void *hdl, off_t offs, int whence);
|
54 |
void close(void *hdl);
|
54 |
void close(void *hdl);
|
55 |
|
55 |
|
56 |
CmdTalk cmd;
|
56 |
CmdTalk cmd;
|
57 |
vector<string> path;
|
57 |
vector<string> path;
|
|
|
58 |
// Host and port part for the URIs we generate.
|
58 |
string httphp;
|
59 |
string httphp;
|
|
|
60 |
// path prefix (this is used to redirect gets to us).
|
59 |
string pathprefix;
|
61 |
string pathprefix;
|
60 |
// mimetype is a constant for a given session, depend on quality
|
62 |
// mimetype is a constant for a given session, depend on quality
|
61 |
// choice only. Initialized once
|
63 |
// choice only. Initialized once
|
62 |
string mimetype;
|
64 |
string mimetype;
|
|
|
65 |
|
|
|
66 |
// Cached uri translation for the vdir: we do this in getinfo()
|
|
|
67 |
// and reuse in open()
|
|
|
68 |
string lastpath;
|
|
|
69 |
string lastmediaurl;
|
|
|
70 |
time_t lasttime;
|
63 |
};
|
71 |
};
|
64 |
|
72 |
|
65 |
bool Tidal::Internal::maybeStartCmd(const string& who)
|
73 |
bool Tidal::Internal::maybeStartCmd(const string& who)
|
66 |
{
|
74 |
{
|
67 |
LOGDEB1("Tidal::maybeStartCmd for: " << who << endl);
|
75 |
LOGDEB1("Tidal::maybeStartCmd for: " << who << endl);
|
|
... |
|
... |
84 |
string Tidal::Internal::get_media_url(const std::string& path)
|
92 |
string Tidal::Internal::get_media_url(const std::string& path)
|
85 |
{
|
93 |
{
|
86 |
if (!maybeStartCmd("get_media_url")) {
|
94 |
if (!maybeStartCmd("get_media_url")) {
|
87 |
return string();
|
95 |
return string();
|
88 |
}
|
96 |
}
|
89 |
|
97 |
if (lastpath.compare(path) || (time(0) - lasttime > 10)) {
|
90 |
unordered_map<string, string> res;
|
98 |
unordered_map<string, string> res;
|
91 |
if (!cmd.callproc("trackuri", {{"path", path}}, res)) {
|
99 |
if (!cmd.callproc("trackuri", {{"path", path}}, res)) {
|
92 |
LOGERR("Tidal::get_media_url: slave failure\n");
|
100 |
LOGERR("Tidal::get_media_url: slave failure\n");
|
93 |
return string();
|
101 |
return string();
|
94 |
}
|
102 |
}
|
95 |
|
103 |
|
96 |
auto it = res.find("media_url");
|
104 |
auto it = res.find("media_url");
|
97 |
if (it == res.end()) {
|
105 |
if (it == res.end()) {
|
98 |
LOGERR("Tidal::get_media_url: no media url in result\n");
|
106 |
LOGERR("Tidal::get_media_url: no media url in result\n");
|
99 |
return string();
|
107 |
return string();
|
100 |
}
|
108 |
}
|
101 |
string& media_url = it->second;
|
109 |
lastmediaurl = it->second;
|
|
|
110 |
lastpath = path;
|
|
|
111 |
lasttime = time(0);
|
|
|
112 |
}
|
|
|
113 |
|
102 |
LOGDEB("Tidal: got media url [" << media_url << "]\n");
|
114 |
LOGDEB("Tidal: got media url [" << lastmediaurl << "]\n");
|
103 |
return media_url;
|
115 |
return lastmediaurl;
|
104 |
}
|
116 |
}
|
105 |
|
117 |
|
106 |
string Tidal::Internal::get_mimetype(const std::string& path)
|
118 |
string Tidal::Internal::get_mimetype(const std::string& path)
|
107 |
{
|
119 |
{
|
108 |
if (!maybeStartCmd("get_mimetype")) {
|
120 |
if (!maybeStartCmd("get_mimetype")) {
|
|
... |
|
... |
124 |
LOGDEB("Tidal: got mimetype [" << mimetype << "]\n");
|
136 |
LOGDEB("Tidal: got mimetype [" << mimetype << "]\n");
|
125 |
}
|
137 |
}
|
126 |
return mimetype;
|
138 |
return mimetype;
|
127 |
}
|
139 |
}
|
128 |
|
140 |
|
129 |
int Tidal::Internal::getinfo(const std::string& path, VirtualDir::FileInfo *inf)
|
|
|
130 |
{
|
|
|
131 |
LOGDEB("Tidal::getinfo: " << path << endl);
|
|
|
132 |
inf->file_length = -1;
|
|
|
133 |
inf->last_modified = 0;
|
|
|
134 |
inf->mime = get_mimetype(path);
|
|
|
135 |
return 0;
|
|
|
136 |
}
|
|
|
137 |
|
|
|
138 |
class StreamHandle {
|
141 |
class StreamHandle {
|
139 |
public:
|
142 |
public:
|
140 |
StreamHandle() : rtmp(nullptr), http_handle(nullptr) {
|
143 |
StreamHandle() : rtmp(nullptr), http_handle(nullptr) {
|
141 |
}
|
144 |
}
|
142 |
~StreamHandle() {
|
145 |
~StreamHandle() {
|
143 |
if (rtmp) {
|
146 |
if (rtmp) {
|
144 |
RTMP_Close(rtmp);
|
147 |
RTMP_Close(rtmp);
|
145 |
RTMP_Free(rtmp);
|
148 |
RTMP_Free(rtmp);
|
146 |
}
|
149 |
}
|
147 |
if (http_handle) {
|
150 |
if (http_handle) {
|
|
|
151 |
LOGDEB("StreamHandle:~: closing http handle\n");
|
148 |
UpnpCloseHttpGet(http_handle);
|
152 |
UpnpCloseHttpGet(http_handle);
|
|
|
153 |
LOGDEB("StreamHandle:~: close done\n");
|
149 |
}
|
154 |
}
|
150 |
}
|
155 |
}
|
151 |
|
156 |
|
152 |
RTMP *rtmp;
|
157 |
RTMP *rtmp;
|
153 |
void *http_handle;
|
158 |
void *http_handle;
|
154 |
int len;
|
159 |
int len;
|
155 |
};
|
160 |
};
|
|
|
161 |
|
|
|
162 |
|
|
|
163 |
int Tidal::Internal::getinfo(const std::string& path, VirtualDir::FileInfo *inf)
|
|
|
164 |
{
|
|
|
165 |
LOGDEB("Tidal::getinfo: " << path << endl);
|
|
|
166 |
string media_url = get_media_url(path);
|
|
|
167 |
if (media_url.empty()) {
|
|
|
168 |
return -1;
|
|
|
169 |
}
|
|
|
170 |
inf->file_length = -1;
|
|
|
171 |
inf->last_modified = 0;
|
|
|
172 |
inf->mime = get_mimetype(path);
|
|
|
173 |
if (media_url.find("http") == 0) {
|
|
|
174 |
void *http_handle;
|
|
|
175 |
char *content_type;
|
|
|
176 |
int content_length;
|
|
|
177 |
int httpstatus;
|
|
|
178 |
int code = UpnpOpenHttpGet(media_url.c_str(), &http_handle,
|
|
|
179 |
&content_type, &content_length,
|
|
|
180 |
&httpstatus, 30);
|
|
|
181 |
LOGDEB("Tidal::getinfo: UpnpOpenHttpGet: ret " << code <<
|
|
|
182 |
" mtype " << content_type << " length " << content_length <<
|
|
|
183 |
" HTTP status " << httpstatus << endl);
|
|
|
184 |
if (code) {
|
|
|
185 |
LOGERR("Tidal::getinfo: UpnpOpenHttpGet: ret " << code <<
|
|
|
186 |
" mtype " << content_type << " length " << content_length <<
|
|
|
187 |
" HTTP status " << httpstatus << endl);
|
|
|
188 |
} else {
|
|
|
189 |
inf->file_length = content_length;
|
|
|
190 |
LOGDEB("Tidal:getinfo: got file length "<< inf->file_length <<endl);
|
|
|
191 |
}
|
|
|
192 |
// Doc says to free this, but it causes malloc issues
|
|
|
193 |
//free(content_type);
|
|
|
194 |
StreamHandle hdl;
|
|
|
195 |
hdl.http_handle = http_handle;
|
|
|
196 |
// Let StreamHandle clean up
|
|
|
197 |
}
|
|
|
198 |
LOGDEB("Tidal::getinfo: returning\n");
|
|
|
199 |
return 0;
|
|
|
200 |
}
|
|
|
201 |
|
156 |
|
202 |
|
157 |
void *Tidal::Internal::open(const string& path)
|
203 |
void *Tidal::Internal::open(const string& path)
|
158 |
{
|
204 |
{
|
159 |
LOGDEB("Tidal::open: " << path << endl);
|
205 |
LOGDEB("Tidal::open: " << path << endl);
|
160 |
string media_url = get_media_url(path);
|
206 |
string media_url = get_media_url(path);
|
|
... |
|
... |
176 |
LOGERR("Tidal::open: UpnpOpenHttpGet: ret " << code <<
|
222 |
LOGERR("Tidal::open: UpnpOpenHttpGet: ret " << code <<
|
177 |
" mtype " << content_type << " length " << content_length <<
|
223 |
" mtype " << content_type << " length " << content_length <<
|
178 |
" HTTP status " << httpstatus << endl);
|
224 |
" HTTP status " << httpstatus << endl);
|
179 |
return nullptr;
|
225 |
return nullptr;
|
180 |
}
|
226 |
}
|
|
|
227 |
// Doc says to free this, but it causes malloc issues
|
|
|
228 |
//free(content_type);
|
181 |
StreamHandle *hdl = new StreamHandle;
|
229 |
StreamHandle *hdl = new StreamHandle;
|
182 |
hdl->http_handle = http_handle;
|
230 |
hdl->http_handle = http_handle;
|
183 |
hdl->len = content_length;
|
231 |
hdl->len = content_length;
|
184 |
return hdl;
|
232 |
return hdl;
|
185 |
} else {
|
233 |
} else {
|
|
... |
|
... |
289 |
|
337 |
|
290 |
static int resultToEntries(const string& encoded, int stidx, int cnt,
|
338 |
static int resultToEntries(const string& encoded, int stidx, int cnt,
|
291 |
std::vector<UpSong>& entries)
|
339 |
std::vector<UpSong>& entries)
|
292 |
{
|
340 |
{
|
293 |
auto decoded = json::parse(encoded);
|
341 |
auto decoded = json::parse(encoded);
|
294 |
LOGDEB("Tidal::browse: got " << decoded.size() << " entries\n");
|
342 |
LOGDEB("Tidal::results: got " << decoded.size() << " entries\n");
|
295 |
LOGDEB1("Tidal::browse: undecoded json: " << decoded.dump() << endl);
|
343 |
LOGDEB1("Tidal::results: undecoded json: " << decoded.dump() << endl);
|
296 |
|
344 |
|
297 |
for (unsigned int i = stidx; i < decoded.size(); i++) {
|
345 |
for (unsigned int i = stidx; i < decoded.size(); i++) {
|
298 |
if (--cnt < 0) {
|
346 |
if (--cnt < 0) {
|
299 |
break;
|
347 |
break;
|
300 |
}
|
348 |
}
|