|
a/src/mediaserver/cdplugins/plgwithslave.cxx |
|
b/src/mediaserver/cdplugins/plgwithslave.cxx |
|
... |
|
... |
65 |
// stuck forever.
|
65 |
// stuck forever.
|
66 |
static const int read_timeout(60);
|
66 |
static const int read_timeout(60);
|
67 |
|
67 |
|
68 |
class PlgWithSlave::Internal {
|
68 |
class PlgWithSlave::Internal {
|
69 |
public:
|
69 |
public:
|
70 |
Internal(PlgWithSlave *_plg, const string& exe, const string& hst,
|
70 |
Internal(PlgWithSlave *_plg, const string& hst,
|
71 |
int prt, const string& pp)
|
71 |
int prt, const string& pp)
|
72 |
: plg(_plg), cmd(read_timeout), exepath(exe), upnphost(hst),
|
72 |
: plg(_plg), cmd(read_timeout), upnphost(hst),
|
73 |
upnpport(prt), pathprefix(pp), laststream(this) { }
|
73 |
upnpport(prt), pathprefix(pp), laststream(this) { }
|
74 |
|
74 |
|
75 |
bool maybeStartCmd();
|
75 |
bool maybeStartCmd();
|
76 |
|
76 |
|
77 |
PlgWithSlave *plg;
|
77 |
PlgWithSlave *plg;
|
78 |
CmdTalk cmd;
|
78 |
CmdTalk cmd;
|
79 |
string exepath;
|
|
|
80 |
// Upnp Host and port. This would only be used to generate URLs *if*
|
79 |
// Upnp Host and port. This would only be used to generate URLs *if*
|
81 |
// we were using the libupnp miniserver. We currently use
|
80 |
// we were using the libupnp miniserver. We currently use
|
82 |
// microhttp because it can do redirects
|
81 |
// microhttp because it can do redirects
|
83 |
string upnphost;
|
82 |
string upnphost;
|
84 |
int upnpport;
|
83 |
int upnpport;
|
|
... |
|
... |
89 |
StreamHandle laststream;
|
88 |
StreamHandle laststream;
|
90 |
};
|
89 |
};
|
91 |
|
90 |
|
92 |
// microhttpd daemon handle. There is only one of these, and one port, we find
|
91 |
// microhttpd daemon handle. There is only one of these, and one port, we find
|
93 |
// the right plugin by looking at the url path.
|
92 |
// the right plugin by looking at the url path.
|
94 |
static struct MHD_Daemon *mhd;
|
93 |
static struct MHD_Daemon *o_mhd;
|
95 |
|
94 |
|
96 |
// Microhttpd connection handler. We re-build the complete url + query
|
95 |
// Microhttpd connection handler. We re-build the complete url + query
|
97 |
// string (&trackid=value), use this to retrieve a service URL
|
96 |
// string (&trackid=value), use this to retrieve a service URL
|
98 |
// (tidal/qobuz...), and redirect to it (HTTP). A previous version
|
97 |
// (tidal/qobuz...), and redirect to it (HTTP). A previous version
|
99 |
// handled rtmp streams, and had to read them. Look up the history if
|
98 |
// handled rtmp streams, and had to read them. Look up the history if
|
|
... |
|
... |
116 |
" version " << version << endl);
|
115 |
" version " << version << endl);
|
117 |
|
116 |
|
118 |
// The 'plgi' here is just whatever plugin started up the httpd task
|
117 |
// The 'plgi' here is just whatever plugin started up the httpd task
|
119 |
// We just use it to find the appropriate plugin for this path,
|
118 |
// We just use it to find the appropriate plugin for this path,
|
120 |
// and then dispatch the request.
|
119 |
// and then dispatch the request.
|
121 |
PlgWithSlave::Internal *plgi = (PlgWithSlave::Internal*)cls;
|
120 |
CDPluginServices *cdpsrv = (CDPluginServices*)cls;
|
122 |
PlgWithSlave *realplg =
|
121 |
PlgWithSlave *realplg =
|
123 |
dynamic_cast<PlgWithSlave*>(plgi->plg->m_services->getpluginforpath(url));
|
122 |
dynamic_cast<PlgWithSlave*>(cdpsrv->getpluginforpath(url));
|
124 |
if (nullptr == realplg) {
|
123 |
if (nullptr == realplg) {
|
125 |
LOGERR("answer_to_connection: no plugin for path [" << url << endl);
|
124 |
LOGERR("answer_to_connection: no plugin for path [" << url << endl);
|
126 |
return MHD_NO;
|
125 |
return MHD_NO;
|
127 |
}
|
126 |
}
|
128 |
|
127 |
|
|
... |
|
... |
179 |
static int accept_policy(void *, const struct sockaddr* sa, socklen_t addrlen)
|
178 |
static int accept_policy(void *, const struct sockaddr* sa, socklen_t addrlen)
|
180 |
{
|
179 |
{
|
181 |
return MHD_YES;
|
180 |
return MHD_YES;
|
182 |
}
|
181 |
}
|
183 |
|
182 |
|
184 |
// Called once for starting the Python program and do other initialization.
|
183 |
// Static
|
185 |
bool PlgWithSlave::Internal::maybeStartCmd()
|
184 |
bool PlgWithSlave::startPluginCmd(CmdTalk& cmd, const string& appname,
|
|
|
185 |
const string& host, unsigned int port,
|
|
|
186 |
const string& pathpref)
|
186 |
{
|
187 |
{
|
187 |
if (cmd.running()) {
|
188 |
string pythonpath = string("PYTHONPATH=") +
|
|
|
189 |
path_cat(g_datadir, "cdplugins") + ":" +
|
|
|
190 |
path_cat(g_datadir, "cdplugins/pycommon") + ":" +
|
|
|
191 |
path_cat(g_datadir, "cdplugins/" + appname);
|
|
|
192 |
string configname = string("UPMPD_CONFIG=") + g_configfilename;
|
|
|
193 |
stringstream ss;
|
|
|
194 |
ss << host << ":" << port;
|
|
|
195 |
string hostport = string("UPMPD_HTTPHOSTPORT=") + ss.str();
|
|
|
196 |
string pp = string("UPMPD_PATHPREFIX=") + pathpref;
|
|
|
197 |
string exepath = path_cat(g_datadir, "cdplugins");
|
|
|
198 |
exepath = path_cat(exepath, appname);
|
|
|
199 |
exepath = path_cat(exepath, appname + "-app" + ".py");
|
|
|
200 |
|
|
|
201 |
if (!cmd.startCmd(exepath, {/*args*/},
|
|
|
202 |
/* env */ {pythonpath, configname, hostport, pp})) {
|
|
|
203 |
LOGERR("PlgWithSlave::maybeStartCmd: startCmd failed\n");
|
|
|
204 |
return false;
|
|
|
205 |
}
|
188 |
return true;
|
206 |
return true;
|
189 |
}
|
207 |
}
|
190 |
|
208 |
|
191 |
int port = CDPluginServices::default_microhttpport();
|
209 |
// Static
|
192 |
string sport;
|
210 |
bool PlgWithSlave::maybeStartMHD(CDPluginServices *cdsrv)
|
193 |
if (plg->m_services->config_get("plgmicrohttpport", sport)) {
|
211 |
{
|
194 |
port = atoi(sport.c_str());
|
|
|
195 |
}
|
|
|
196 |
if (nullptr == mhd) {
|
212 |
if (nullptr == o_mhd) {
|
197 |
|
213 |
int port = CDPluginServices::microhttpport();
|
198 |
// Start the microhttpd daemon. There can be only one, and it
|
214 |
// Start the microhttpd daemon. There can be only one, and it
|
199 |
// is started with a context handle which points to whatever
|
215 |
// is started with a context handle which points to whatever
|
200 |
// plugin got there first. The callback will only use the
|
216 |
// plugin got there first. The callback will only use the
|
201 |
// handle to get to the plugin services, and retrieve the
|
217 |
// handle to get to the plugin services, and retrieve the
|
202 |
// appropriate plugin based on the url path prefix.
|
218 |
// appropriate plugin based on the url path prefix.
|
203 |
LOGDEB("PlgWithSlave: starting httpd on port "<< port << endl);
|
219 |
LOGDEB("PlgWithSlave: starting httpd on port "<< port << endl);
|
204 |
mhd = MHD_start_daemon(
|
220 |
o_mhd = MHD_start_daemon(
|
205 |
MHD_USE_THREAD_PER_CONNECTION,
|
221 |
MHD_USE_THREAD_PER_CONNECTION,
|
206 |
//MHD_USE_SELECT_INTERNALLY,
|
222 |
//MHD_USE_SELECT_INTERNALLY,
|
207 |
port,
|
223 |
port,
|
208 |
/* Accept policy callback and arg */
|
224 |
/* Accept policy callback and arg */
|
209 |
accept_policy, NULL,
|
225 |
accept_policy, NULL,
|
210 |
/* handler and arg */
|
226 |
/* handler and arg */
|
211 |
&answer_to_connection, this,
|
227 |
&answer_to_connection, cdsrv,
|
212 |
MHD_OPTION_END);
|
228 |
MHD_OPTION_END);
|
213 |
if (nullptr == mhd) {
|
229 |
if (nullptr == o_mhd) {
|
214 |
LOGERR("PlgWithSlave: MHD_start_daemon failed\n");
|
230 |
LOGERR("PlgWithSlave: MHD_start_daemon failed\n");
|
215 |
return false;
|
231 |
return false;
|
216 |
}
|
232 |
}
|
217 |
}
|
233 |
}
|
|
|
234 |
return true;
|
|
|
235 |
}
|
|
|
236 |
|
|
|
237 |
// Called once for starting the Python program and do other initialization.
|
|
|
238 |
bool PlgWithSlave::Internal::maybeStartCmd()
|
|
|
239 |
{
|
|
|
240 |
if (cmd.running()) {
|
|
|
241 |
return true;
|
218 |
|
242 |
}
|
219 |
string pythonpath = string("PYTHONPATH=") +
|
243 |
if (!maybeStartMHD(this->plg->m_services)) {
|
220 |
path_cat(g_datadir, "cdplugins") + ":" +
|
|
|
221 |
path_cat(g_datadir, "cdplugins/pycommon") + ":" +
|
|
|
222 |
path_cat(g_datadir, "cdplugins/" + plg->m_name);
|
|
|
223 |
string configname = string("UPMPD_CONFIG=") + g_configfilename;
|
|
|
224 |
stringstream ss;
|
|
|
225 |
ss << upnphost << ":" << port;
|
|
|
226 |
string hostport = string("UPMPD_HTTPHOSTPORT=") + ss.str();
|
|
|
227 |
string pp = string("UPMPD_PATHPREFIX=") + pathprefix;
|
|
|
228 |
if (!cmd.startCmd(exepath, {/*args*/},
|
|
|
229 |
/* env */ {pythonpath, configname, hostport, pp})) {
|
|
|
230 |
LOGERR("PlgWithSlave::maybeStartCmd: startCmd failed\n");
|
|
|
231 |
return false;
|
244 |
return false;
|
232 |
}
|
245 |
}
|
233 |
return true;
|
246 |
int port = CDPluginServices::microhttpport();
|
|
|
247 |
return startPluginCmd(cmd, plg->m_name, upnphost, port, pathprefix);
|
234 |
}
|
248 |
}
|
235 |
|
249 |
|
236 |
bool PlgWithSlave::startInit()
|
250 |
bool PlgWithSlave::startInit()
|
237 |
{
|
251 |
{
|
238 |
return m && m->maybeStartCmd();
|
252 |
return m && m->maybeStartCmd();
|
|
... |
|
... |
279 |
|
293 |
|
280 |
|
294 |
|
281 |
PlgWithSlave::PlgWithSlave(const string& name, CDPluginServices *services)
|
295 |
PlgWithSlave::PlgWithSlave(const string& name, CDPluginServices *services)
|
282 |
: CDPlugin(name, services)
|
296 |
: CDPlugin(name, services)
|
283 |
{
|
297 |
{
|
284 |
string exepath = path_cat(g_datadir, "cdplugins");
|
|
|
285 |
exepath = path_cat(exepath, name);
|
|
|
286 |
exepath = path_cat(exepath, name + "-app" + ".py");
|
|
|
287 |
|
|
|
288 |
m = new Internal(this, exepath,
|
298 |
m = new Internal(this,
|
289 |
services->getupnpaddr(this),
|
299 |
services->getupnpaddr(this),
|
290 |
services->getupnpport(this),
|
300 |
services->getupnpport(this),
|
291 |
services->getpathprefix(this));
|
301 |
services->getpathprefix(this));
|
292 |
}
|
302 |
}
|
293 |
|
303 |
|