Switch to unified view

a/src/cdplugins/tidal.cxx b/src/cdplugins/tidal.cxx
...
...
36
using namespace std;
36
using namespace std;
37
using namespace std::placeholders;
37
using namespace std::placeholders;
38
using json = nlohmann::json;
38
using json = nlohmann::json;
39
using namespace UPnPProvider;
39
using namespace UPnPProvider;
40
40
41
class StreamHandle {
42
public:
43
    StreamHandle() : rtmp(nullptr), http_handle(nullptr), offset(0) {
44
    }
45
    ~StreamHandle() {
46
        clear();
47
    }
48
    void clear() {
49
        if (rtmp) {
50
      RTMP_Close(rtmp);
51
      RTMP_Free(rtmp);
52
  }
53
  if (http_handle) {
54
            LOGDEB("StreamHandle:~: closing http handle\n");
55
      UpnpCloseHttpGet(http_handle);
56
            LOGDEB("StreamHandle:~: close done\n");
57
  }
58
        len = 0;
59
        offset = 0;
60
        media_url.clear();
61
        path.clear();
62
    }
63
    string path;
64
    string media_url;
65
    RTMP *rtmp;
66
    void *http_handle;
67
    int len;
68
    off_t offset;
69
    time_t opentime;
70
};
71
41
class Tidal::Internal {
72
class Tidal::Internal {
42
public:
73
public:
43
    Internal(const vector<string>& pth, const string& hp, const string& pp)
74
    Internal(const vector<string>& pth, const string& hp, const string& pp)
44
    : path(pth), httphp(hp), pathprefix(pp), lasttime(0) { }
75
    : path(pth), httphp(hp), pathprefix(pp) { }
45
76
46
    bool maybeStartCmd(const string&);
77
    bool maybeStartCmd(const string&);
47
    string get_media_url(const std::string& path);
78
    string get_media_url(const std::string& path);
48
    string get_mimetype(const std::string& path);
79
    string get_mimetype(const std::string& path);
49
    
80
    
...
...
61
    string pathprefix;
92
    string pathprefix;
62
    // mimetype is a constant for a given session, depend on quality
93
    // mimetype is a constant for a given session, depend on quality
63
    // choice only. Initialized once
94
    // choice only. Initialized once
64
    string mimetype;
95
    string mimetype;
65
96
66
    // Cached uri translation for the vdir: we do this in getinfo()
97
    // Cached uri translation and stream: set in getinfo() and reused
67
    // and reuse in open()
98
    // in open()
68
    string lastpath;
99
    StreamHandle laststream;
69
    string lastmediaurl;
70
    time_t lasttime;
71
};
100
};
72
101
73
bool Tidal::Internal::maybeStartCmd(const string& who)
102
bool Tidal::Internal::maybeStartCmd(const string& who)
74
{
103
{
75
    LOGDEB1("Tidal::maybeStartCmd for: " << who << endl);
104
    LOGDEB1("Tidal::maybeStartCmd for: " << who << endl);
...
...
87
    }
116
    }
88
    }
117
    }
89
    return true;
118
    return true;
90
}
119
}
91
120
92
string Tidal::Internal::get_media_url(const std::string& path)
93
{
94
    if (!maybeStartCmd("get_media_url")) {
95
  return string();
96
    }
97
    if (lastpath.compare(path) || (time(0) - lasttime > 10)) {
98
  unordered_map<string, string> res;
99
  if (!cmd.callproc("trackuri", {{"path", path}}, res)) {
100
      LOGERR("Tidal::get_media_url: slave failure\n");
101
      return string();
102
  }
103
104
  auto it = res.find("media_url");
105
  if (it == res.end()) {
106
      LOGERR("Tidal::get_media_url: no media url in result\n");
107
      return string();
108
  }
109
  lastmediaurl = it->second;
110
  lastpath = path;
111
  lasttime = time(0);
112
    }
113
114
    LOGDEB("Tidal: got media url [" << lastmediaurl << "]\n");
115
    return lastmediaurl;
116
}
117
118
string Tidal::Internal::get_mimetype(const std::string& path)
121
string Tidal::Internal::get_mimetype(const std::string& path)
119
{
122
{
120
    if (!maybeStartCmd("get_mimetype")) {
123
    if (!maybeStartCmd("get_mimetype")) {
121
    return string();
124
    return string();
122
    }
125
    }
...
...
136
    LOGDEB("Tidal: got mimetype [" << mimetype << "]\n");
139
    LOGDEB("Tidal: got mimetype [" << mimetype << "]\n");
137
    }
140
    }
138
    return mimetype;
141
    return mimetype;
139
}
142
}
140
143
141
class StreamHandle {
144
string Tidal::Internal::get_media_url(const std::string& path)
142
public:
145
{
143
    StreamHandle() : rtmp(nullptr), http_handle(nullptr) {
146
    if (!maybeStartCmd("get_media_url")) {
147
  return string();
144
    }
148
    }
145
    ~StreamHandle() {
149
    time_t now = time(0);
146
  if (rtmp) {
150
    if (laststream.path.compare(path) || (now - laststream.opentime > 10)) {
147
      RTMP_Close(rtmp);
151
  unordered_map<string, string> res;
148
      RTMP_Free(rtmp);
152
  if (!cmd.callproc("trackuri", {{"path", path}}, res)) {
149
  }
153
      LOGERR("Tidal::get_media_url: slave failure\n");
150
  if (http_handle) {
154
      return string();
151
            LOGDEB("StreamHandle:~: closing http handle\n");
152
      UpnpCloseHttpGet(http_handle);
153
            LOGDEB("StreamHandle:~: close done\n");
154
  }
155
    }
156
    
155
    }
157
    RTMP *rtmp;
156
158
    void *http_handle;
157
  auto it = res.find("media_url");
159
    int len;
158
  if (it == res.end()) {
159
      LOGERR("Tidal::get_media_url: no media url in result\n");
160
      return string();
161
  }
162
        laststream.clear();
163
        laststream.path = path;
164
        laststream.media_url = it->second;
165
        laststream.opentime = now;
166
    }
167
168
    LOGDEB("Tidal: got media url [" << laststream.media_url << "]\n");
169
    return laststream.media_url;
160
};
170
}
161
162
171
163
int Tidal::Internal::getinfo(const std::string& path, VirtualDir::FileInfo *inf)
172
int Tidal::Internal::getinfo(const std::string& path, VirtualDir::FileInfo *inf)
164
{
173
{
165
    LOGDEB("Tidal::getinfo: " << path << endl);
174
    LOGDEB("Tidal::getinfo: " << path << endl);
175
176
    laststream.clear();
166
    string media_url = get_media_url(path);
177
    string media_url = get_media_url(path);
167
    if (media_url.empty()) {
178
    if (media_url.empty()) {
168
    return -1;
179
    return -1;
169
    }
180
    }
170
    inf->file_length = -1;
181
    inf->file_length = -1;
171
    inf->last_modified = 0;
182
    inf->last_modified = 0;
172
    inf->mime = get_mimetype(path);
183
    inf->mime = get_mimetype(path);
173
    if (media_url.find("http") == 0) {
184
    if (media_url.find("http") == 0) {
174
  void *http_handle;
175
    char *content_type;
185
    char *content_type;
176
  int content_length;
177
    int httpstatus;
186
    int httpstatus;
178
    int code = UpnpOpenHttpGet(media_url.c_str(), &http_handle,
187
    int code = UpnpOpenHttpGet(media_url.c_str(), &laststream.http_handle,
179
                     &content_type, &content_length,
188
                     &content_type, &laststream.len,
180
                     &httpstatus, 30);
189
                     &httpstatus, 30);
181
    LOGDEB("Tidal::getinfo: UpnpOpenHttpGet: ret " << code <<
190
    LOGDEB("Tidal::getinfo: UpnpOpenHttpGet: ret " << code <<
182
           " mtype " << content_type << " length " << content_length <<
191
           " mtype " << content_type << " length " << laststream.len <<
183
           " HTTP status " << httpstatus << endl);
192
           " HTTP status " << httpstatus << endl);
184
    if (code) {
193
    if (code) {
185
        LOGERR("Tidal::getinfo: UpnpOpenHttpGet: ret " << code <<
194
        LOGERR("Tidal::getinfo: UpnpOpenHttpGet: ret " << code <<
186
           " mtype " << content_type << " length " << content_length <<
195
           " mtype " << content_type << " length " << laststream.len <<
187
           " HTTP status " << httpstatus << endl);
196
           " HTTP status " << httpstatus << endl);
188
    } else {
197
    } else {
189
        inf->file_length = content_length;
198
        inf->file_length = laststream.len;
190
        LOGDEB("Tidal:getinfo: got file length "<< inf->file_length <<endl);
199
        LOGDEB("Tidal:getinfo: got file length "<< inf->file_length <<endl);
191
    }
200
    }
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
    }
201
    }
198
    LOGDEB("Tidal::getinfo: returning\n");
202
    LOGDEB("Tidal::getinfo: returning\n");
199
    return 0;
203
    return 0;
200
}
204
}
201
202
205
203
void *Tidal::Internal::open(const string& path)
206
void *Tidal::Internal::open(const string& path)
204
{
207
{
205
    LOGDEB("Tidal::open: " << path << endl);
208
    LOGDEB("Tidal::open: " << path << endl);
206
    string media_url = get_media_url(path);
209
    string media_url = get_media_url(path);
207
    if (media_url.empty()) {
210
    if (media_url.empty()) {
208
    return nullptr;
211
    return nullptr;
209
    }
212
    }
210
    if (media_url.find("http") == 0) {
213
    if (media_url.find("http") == 0) {
211
  void *http_handle;
214
        if (laststream.http_handle == nullptr) {
212
  char *content_type;
215
            char *content_type;
213
  int content_length;
216
            int httpstatus;
214
  int httpstatus;
215
  int code = UpnpOpenHttpGet(media_url.c_str(), &http_handle,
217
            int code = UpnpOpenHttpGet(media_url.c_str(),
216
                   &content_type, &content_length,
218
                                       &laststream.http_handle,
217
                   &httpstatus, 30);
219
                                       &content_type, &laststream.len,
220
                                       &httpstatus, 30);
218
  LOGDEB("Tidal::open: UpnpOpenHttpGet: ret " << code <<
221
            LOGDEB("Tidal::open: UpnpOpenHttpGet: ret " << code <<
219
         " mtype " << content_type << " length " << content_length <<
222
                   " mtype " << content_type << " length " <<
223
                   laststream.len <<
220
         " HTTP status " << httpstatus << endl);
224
                   " HTTP status " << httpstatus << endl);
221
  if (code) {
225
            if (code) {
222
      LOGERR("Tidal::open: UpnpOpenHttpGet: ret " << code <<
226
                LOGERR("Tidal::open: UpnpOpenHttpGet: ret " << code <<
223
         " mtype " << content_type << " length " << content_length <<
227
                       " mtype " << content_type << " length " <<
228
                       laststream.len <<
224
         " HTTP status " << httpstatus << endl);
229
                       " HTTP status " << httpstatus << endl);
225
      return nullptr;
230
                return nullptr;
226
  }
231
            }
232
        } 
227
        // Doc says to free this, but it causes malloc issues
233
        // Doc says to free content_type, but it causes malloc issues
228
    //free(content_type);
234
    //free(content_type);
229
    StreamHandle *hdl = new StreamHandle;
235
    StreamHandle *hdl = new StreamHandle;
230
    hdl->http_handle = http_handle;
236
    hdl->http_handle = laststream.http_handle;
231
  hdl->len = content_length;
237
  hdl->len = laststream.len;
238
        hdl->media_url = media_url;
239
        laststream.http_handle = nullptr;
240
        laststream.clear();
232
    return hdl;
241
    return hdl;
233
    } else {
242
    } else {
234
    RTMP *rtmp = RTMP_Alloc();
243
    RTMP *rtmp = RTMP_Alloc();
235
    RTMP_Init(rtmp);
244
    RTMP_Init(rtmp);
236
245
...
...
265
    if (!_hdl)
274
    if (!_hdl)
266
    return -1;
275
    return -1;
267
276
268
    // The pupnp http code has a default 1MB buffer size which is much
277
    // The pupnp http code has a default 1MB buffer size which is much
269
    // too big for us (too slow, esp. because tidal will stall).
278
    // too big for us (too slow, esp. because tidal will stall).
270
    if (cnt > 100 * 1024)
279
    const int mybsize = 200 * 1024;
271
      cnt = 100 * 1024;
280
    if (cnt > mybsize)
281
        cnt = mybsize;
272
282
273
    StreamHandle *hdl = (StreamHandle *)_hdl;
283
    StreamHandle *hdl = (StreamHandle *)_hdl;
274
284
275
    if (hdl->rtmp) {
285
    if (hdl->rtmp) {
276
    RTMP *rtmp = hdl->rtmp;
286
    RTMP *rtmp = hdl->rtmp;
...
...
281
        if (didread <= 0)
291
        if (didread <= 0)
282
        break;
292
        break;
283
        totread += didread;
293
        totread += didread;
284
    }
294
    }
285
    LOGDEB("Tidal::read: total read: " << totread << endl);
295
    LOGDEB("Tidal::read: total read: " << totread << endl);
296
        hdl->offset += totread;
286
    return totread > 0 ? totread : -1;
297
    return totread > 0 ? totread : -1;
287
    } else if (hdl->http_handle) {
298
    } else if (hdl->http_handle) {
288
    int code = UpnpReadHttpGet(hdl->http_handle, buf, &cnt, 30);
299
    int code = UpnpReadHttpGet(hdl->http_handle, buf, &cnt, 30);
289
    if (code) {
300
    if (code) {
290
        LOGERR("Tidal::read: UpnpReadHttpGet returned " << code << endl);
301
        LOGERR("Tidal::read: UpnpReadHttpGet returned " << code << endl);
291
        return -1;
302
        return -1;
292
    }
303
    }
304
        hdl->offset += cnt;
293
    return int(cnt);
305
    return int(cnt);
294
    } else {
306
    } else {
295
    LOGERR("Tidal::read: neither rtmp nor http\n");
307
    LOGERR("Tidal::read: neither rtmp nor http\n");
296
    return -1;
308
    return -1;
297
    }
309
    }
298
}
310
}
299
311
300
off_t Tidal::Internal::seek(void *hdl, off_t offs, int whence)
312
off_t Tidal::Internal::seek(void *_hdl, off_t offs, int whence)
301
{
313
{
302
    LOGDEB("Tidal::seek\n");
314
    StreamHandle *hdl = (StreamHandle *)_hdl;
315
    LOGDEB("Tidal::seek: offs "<< offs << " whence " << whence << " current " <<
316
           hdl->offset << endl);
317
    if (whence == 0) {
318
        hdl->offset = offs;
319
    } else if (whence == 1) {
320
        hdl->offset += offs;
321
    } else if (whence == 2) {
322
        hdl->offset = hdl->len + offs;
323
    }
324
    if (hdl->http_handle) {
325
        UpnpCloseHttpGet(hdl->http_handle);
326
  char *content_type;
327
  int content_length;
328
  int httpstatus;
329
  int code = UpnpOpenHttpGetEx(hdl->media_url.c_str(), &hdl->http_handle,
330
                   &content_type, &content_length,
331
                   &httpstatus, hdl->offset, 2000000000, 30);
332
        LOGERR("Tidal::seek to " << hdl->offset <<
333
               " UpnpOpenHttpGetEx: ret " << code <<
334
               " mtype " << content_type << " length " << content_length <<
335
               " HTTP status " << httpstatus << endl);
336
  if (code) {
337
      LOGERR("Tidal::seek to " << hdl->offset <<
338
                   " UpnpOpenHttpGetEx: ret " << code <<
339
         " mtype " << content_type << " length " << content_length <<
340
         " HTTP status " << httpstatus << endl);
341
            return -1;
342
  } 
343
    }
344
    
303
    return -1;
345
    return 0;
304
}
346
}
305
347
306
void Tidal::Internal::close(void *_hdl)
348
void Tidal::Internal::close(void *_hdl)
307
{
349
{
308
    LOGDEB("Tidal::close\n");
350
    LOGDEB("Tidal::close\n");