--- a/src/cdplugins/tidal.cxx
+++ b/src/cdplugins/tidal.cxx
@@ -17,50 +17,193 @@
#include "tidal.hxx"
+#define LOGGER_LOCAL_LOGINC 3
+
#include <string>
#include <vector>
+#include <string.h>
+#include <librtmp/rtmp.h>
#include "cmdtalk.h"
#include "pathut.h"
#include "log.hxx"
#include "json.hpp"
+#include "main.hxx"
using namespace std;
-extern string g_datadir, g_configfilename;
-
+using namespace std::placeholders;
using json = nlohmann::json;
+using namespace UPnPProvider;
class Tidal::Internal {
public:
- Internal(const vector<string>& pth)
- : path(pth) {
- }
+ Internal(const vector<string>& pth, const string& hp, const string& pp)
+ : path(pth), httphp(hp), pathprefix(pp) { }
+
+ bool maybeStartCmd(const string&);
+ string get_media_url(const std::string& path);
+ string get_mimetype(const std::string& path);
+
+ int getinfo(const std::string&, VirtualDir::FileInfo*);
+ void *open(const std::string&);
+ int read(void *hdl, char* buf, size_t cnt);
+ off_t seek(void *hdl, off_t offs, int whence);
+ void close(void *hdl);
+
CmdTalk cmd;
- bool maybeStartCmd() {
- LOGDEB("Tidal::maybeStartCmd\n");
- if (!cmd.running()) {
- string pythonpath = string("PYTHONPATH=") +
- path_cat(g_datadir, "cdplugins/pycommon");
- string configname = string("UPMPD_CONFIG=") +
- g_configfilename;
- LOGDEB("Tidal::maybeStartCmd: calling startCmd\n");
- if (!cmd.startCmd("tidal.py", {/*args*/},
- {pythonpath, configname}, path)) {
- LOGDEB("Tidal::maybeStartCmd: startCmd failed\n");
- return false;
- }
- LOGDEB("Tidal::maybeStartCmd: startCmd ok\n");
- }
- LOGDEB("Tidal::maybeStartCmd: cmd running\n");
- return true;
- }
-
vector<string> path;
+ string httphp;
+ string pathprefix;
+ // mimetype is a constant for a given session, depend on quality
+ // choice only. Initialized once
+ string mimetype;
};
-Tidal::Tidal(const vector<string>& plgpath)
- : m(new Internal(plgpath))
+bool Tidal::Internal::maybeStartCmd(const string& who)
+{
+ LOGDEB1("Tidal::maybeStartCmd for: " << who << endl);
+ if (!cmd.running()) {
+ string pythonpath = string("PYTHONPATH=") +
+ path_cat(g_datadir, "cdplugins/pycommon");
+ string configname = string("UPMPD_CONFIG=") + g_configfilename;
+ string hostport = string("UPMPD_HTTPHOSTPORT=") + httphp;
+ string pp = string("UPMPD_PATHPREFIX=") + pathprefix;
+ if (!cmd.startCmd("tidal.py", {/*args*/},
+ {pythonpath, configname, hostport, pp},
+ path)) {
+ LOGERR("Tidal::maybeStartCmd: " << who << " startCmd failed\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+string Tidal::Internal::get_media_url(const std::string& path)
+{
+ if (!maybeStartCmd("get_media_url")) {
+ return string();
+ }
+
+ unordered_map<string, string> res;
+ if (!cmd.callproc("trackuri", {{"path", path}}, res)) {
+ LOGERR("Tidal::get_media_url: slave failure\n");
+ return string();
+ }
+
+ auto it = res.find("media_url");
+ if (it == res.end()) {
+ LOGERR("Tidal::get_media_url: no media url in result\n");
+ return string();
+ }
+ string& media_url = it->second;
+ LOGDEB("Tidal: got media url [" << media_url << "]\n");
+ return media_url;
+}
+
+string Tidal::Internal::get_mimetype(const std::string& path)
+{
+ if (!maybeStartCmd("get_mimetype")) {
+ return string();
+ }
+ if (mimetype.empty()) {
+ unordered_map<string, string> res;
+ if (!cmd.callproc("mimetype", {{"path", path}}, res)) {
+ LOGERR("Tidal::get_media_url: slave failure\n");
+ return string();
+ }
+
+ auto it = res.find("mimetype");
+ if (it == res.end()) {
+ LOGERR("Tidal::get_mimetype: no mimetype in result\n");
+ return string();
+ }
+ mimetype = it->second;
+ LOGDEB("Tidal: got mimetype [" << mimetype << "]\n");
+ }
+ return mimetype;
+}
+
+int Tidal::Internal::getinfo(const std::string& path, VirtualDir::FileInfo *inf)
+{
+ LOGDEB("Tidal::getinfo: " << path << endl);
+ inf->file_length = -1;
+ inf->last_modified = 0;
+ inf->mime = get_mimetype(path);
+ return 0;
+}
+
+void *Tidal::Internal::open(const string& path)
+{
+ LOGDEB("Tidal::open: " << path << endl);
+ string media_url = get_media_url(path);
+ if (media_url.empty()) {
+ return nullptr;
+ }
+ RTMP *rtmp = RTMP_Alloc();
+ RTMP_Init(rtmp);
+
+ // Writable copy of url
+ if (!RTMP_SetupURL(rtmp, strdup(media_url.c_str()))) {
+ LOGERR("Tidal::open: RTMP_SetupURL failed for [" <<
+ media_url << "]\n");
+ RTMP_Free(rtmp);
+ return nullptr;
+ }
+ if (!RTMP_Connect(rtmp, NULL)) {
+ LOGERR("Tidal::open: RTMP_Connect failed for [" <<
+ media_url << "]\n");
+ RTMP_Free(rtmp);
+ return nullptr;
+ }
+ if (!RTMP_ConnectStream(rtmp, 0)) {
+ LOGERR("Tidal::open: RTMP_ConnectStream failed for [" <<
+ media_url << "]\n");
+ RTMP_Free(rtmp);
+ return nullptr;
+ }
+
+ return rtmp;
+}
+
+int Tidal::Internal::read(void *hdl, char* buf, size_t cnt)
+{
+ LOGDEB("Tidal::read: " << cnt << endl);
+ if (!hdl)
+ return -1;
+ RTMP *rtmp = (RTMP *)hdl;
+ size_t totread = 0;
+ while (totread < cnt) {
+ int didread = RTMP_Read(rtmp, buf+totread, cnt-totread);
+ LOGDEB("Tidal::read: RTMP_Read returned: " << didread << endl);
+ if (didread <= 0)
+ break;
+ totread += didread;
+ }
+ LOGDEB("Tidal::read: total read: " << totread << endl);
+ return totread > 0 ? totread : -1;
+}
+
+off_t Tidal::Internal::seek(void *hdl, off_t offs, int whence)
+{
+ LOGDEB("Tidal::seek\n");
+ return -1;
+}
+
+void Tidal::Internal::close(void *hdl)
+{
+ LOGDEB("Tidal::close\n");
+ if (hdl) {
+ RTMP *rtmp = (RTMP *)hdl;
+ RTMP_Close(rtmp);
+ RTMP_Free(rtmp);
+ }
+}
+
+
+Tidal::Tidal(const vector<string>& plgpath, const string& httphp,
+ const string& pp)
+ : m(new Internal(plgpath, httphp, pp))
{
}
@@ -75,8 +218,7 @@
BrowseFlag flg)
{
LOGDEB("Tidal::browse\n");
- if (!m->maybeStartCmd()) {
- LOGERR("Tidal::browse: startcmd failed\n");
+ if (!m->maybeStartCmd("browse")) {
return -1;
}
unordered_map<string, string> res;
@@ -133,3 +275,16 @@
}
return decoded.size();
}
+
+VirtualDir::FileOps Tidal::getFileOps()
+{
+ VirtualDir::FileOps ops;
+
+ ops.getinfo = bind(&Tidal::Internal::getinfo, m, _1, _2);
+ ops.open = bind(&Tidal::Internal::open, m, _1);
+ ops.read = bind(&Tidal::Internal::read, m, _1, _2, _3);
+ ops.seek = bind(&Tidal::Internal::seek, m, _1, _2, _3);
+ ops.close = bind(&Tidal::Internal::close, m, _1);
+ return ops;
+}
+