Skip to content

Commit

Permalink
Introduced experimental script support:
Browse files Browse the repository at this point in the history
If url contains "resolve://" protocol than it's treated by
serviceapp specially.
It runs script located in /etc/enigma2/script, with arguments
separated by '|' delimiter.

For example
Will run /etc/enigma2/script arg1 arg2

Script should return playurl on standard output, it will
be than played by serviceapp

Co-authored-by: lprot <lprot@list.ru>
  • Loading branch information
mx3L and lprot committed Apr 22, 2018
1 parent dabd627 commit e461ca6
Show file tree
Hide file tree
Showing 5 changed files with 322 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/serviceapp/Makefile.am
Expand Up @@ -18,6 +18,7 @@ plugin_LTLIBRARIES = serviceapp.la
serviceapp_la_SOURCES = \
serviceapp.cpp \
extplayer.cpp \
scriptrun.cpp \
myconsole.cpp \
wrappers.cpp \
m3u8.cpp \
Expand Down
186 changes: 186 additions & 0 deletions src/serviceapp/scriptrun.cpp
@@ -0,0 +1,186 @@
#include "scriptrun.h"
#include "common.h"

void scriptrun::stdoutAvail(const char *data)
{
//eDebug("outData: %s", data);
m_stdout.append(data);
}

void scriptrun::stderrAvail(const char *data)
{
//eDebug("errData: %s", data);
m_stderr.append(data);
}

void scriptrun::appClosed(int retval)
{
//eDebug("scriptrun::appClosed: %d", retval);
scriptEnded(retval);
}

scriptrun::scriptrun(const std::string &scriptPath, const std::vector<std::string> &params)
{
m_scriptpath = scriptPath;
m_params = params;
}

scriptrun::~scriptrun()
{
stop();
}

void scriptrun::run(eMainloop *context)
{
m_console = new eConsoleContainer();
CONNECT(m_console->appClosed, scriptrun::appClosed);
CONNECT(m_console->stdoutAvail, scriptrun::stdoutAvail);
CONNECT(m_console->stderrAvail, scriptrun::stderrAvail);

std::vector<std::string> args;
args.push_back(m_scriptpath);
for (size_t i = 0; i < m_params.size(); i++)
args.push_back(m_params[i]);

char **cargs = (char **) malloc(sizeof(char *) * args.size()+1);
for (size_t i=0; i <= args.size(); i++)
{
// execvp needs args array terminated with NULL
if (i == args.size())
{
cargs[i] = NULL;
eDebugNoNewLine("\n");
}
else
{
cargs[i] = strdup(args[i].c_str());
if (i != 0 && cargs[i][0] != '-')
eDebugNoNewLine("\"%s\" ", cargs[i]);
else
eDebugNoNewLine("%s ", cargs[i]);
}
}
m_console->execute(context, cargs[0], cargs);
}

void scriptrun::stop()
{
if (m_console && m_console->running())
m_console->sendCtrlC();
}

ResolveUrl::ResolveUrl(const std::string &url):

m_url(url),
m_success(0),
mStopped(false),
mThreadRunning(false),
mMessageMain(eApp, 1),
mMessageThread(this, 1)
{
eDebug("ResolveUrl::ResolveUrl %s", url.c_str());
CONNECT(mMessageThread.recv_msg, ResolveUrl::gotMessage);
CONNECT(mMessageMain.recv_msg, ResolveUrl::gotMessage);
pthread_mutex_init(&mWaitForStopMutex, NULL);
pthread_cond_init(&mWaitForStopCond, NULL);
}

ResolveUrl::~ResolveUrl()
{
stop();
pthread_mutex_destroy(&mWaitForStopMutex);
pthread_cond_destroy(&mWaitForStopCond);
delete m_scriptrun;
}

void ResolveUrl::start()
{
std::vector<std::string> params;
std::string delimiter = "|";
size_t last = 0; size_t next = 0;
while ((next = m_url.find(delimiter, last)) != std::string::npos)
{
params.push_back(m_url.substr(last, next-last));
last = next + 1;
}
params.push_back(m_url.substr(last));

m_scriptrun = new scriptrun("/etc/enigma2/script", params);

// start player only when mainloop in player thread has started
mMessageThread.send(Message(Message::tStart));
run();
}

void ResolveUrl::stop()
{
mStopped = true;
if (mThreadRunning)
{
mMessageThread.send(Message(Message::tStop));
}
kill();
}

std::string ResolveUrl::getUrl()
{
std::string url = m_scriptrun->getStdOut();
url = url.substr(0, url.size() - 1);
return url;
}

void ResolveUrl::scriptEnded(int retval)
{
pthread_mutex_lock(&mWaitForStopMutex);
if (mWaitForStop)
{
mWaitForStop = false;
pthread_cond_signal(&mWaitForStopCond);
}
pthread_mutex_unlock(&mWaitForStopMutex);
quit(0);
if (mStopped)
m_success = false;
else
{
m_success = !retval;
if (m_success)
m_success = !getUrl().empty();
}
mMessageMain.send(Message(Message::stop));
}

void ResolveUrl::thread()
{
mThreadRunning = true;
hasStarted();
runLoop();
}

void ResolveUrl::thread_finished()
{
//eDebug("ResolveUrl::thread_finished");
mThreadRunning = false;
}

void ResolveUrl::gotMessage(const ResolveUrl::Message &message)
{
switch (message.type)
{
case Message::tStart:
//eDebug("ResolveUrl::gotMessage - tStart");
CONNECT(m_scriptrun->scriptEnded, ResolveUrl::scriptEnded);
m_scriptrun->run(this);
break;
case Message::tStop:
eDebug("ResolveUrl::gotMessage - tStop");
m_scriptrun->stop();
break;
case Message::stop:
eDebug("ResolveUrl::gotMessage - stop");
urlResolved(m_success);
break;
default:
break;
}
}
94 changes: 94 additions & 0 deletions src/serviceapp/scriptrun.h
@@ -0,0 +1,94 @@
#ifndef SCRIPTRUN_H
#define SCRIPTRUN_H

#include <lib/base/ebase.h>
#include <lib/base/message.h>
#include <lib/python/connections.h>
#include <lib/base/thread.h>

#include "extplayer.h"
#include "myconsole.h"

#if SIGCXX_MAJOR_VERSION == 2
class scriptrun: public sigc::trackable
#else
class scriptrun: public Object
#endif
{
std::vector<std::string> m_params;
std::string m_scriptpath;
std::string m_stdout;
std::string m_stderr;
ePtr<eConsoleContainer> m_console;

void stdoutAvail(const char *data);
void stderrAvail(const char *data);
void appClosed(int retval);
public:
scriptrun(const std::string &scriptPath,
const std::vector<std::string> &params);
~scriptrun();
void run(eMainloop *context);
void stop();

std::string getStdOut(){return m_stdout;}
std::string getStdErr(){return m_stderr;}

PSignal1<void, int> scriptEnded;
};


#if SIGCXX_MAJOR_VERSION == 2
class ResolveUrl: public sigc::trackable, public eThread, public eMainloop
#else
class ResolveUrl: public Object, public eThread, public eMainloop
#endif
{
struct Message
{
int type;
enum
{
start,
tStart,
stop,
tStop,
};
Message(int type)
:type(type) {}
};
scriptrun *m_scriptrun;
std::string m_url;
int m_success;

bool mThreadRunning;
bool mStopped;

eFixedMessagePump<Message> mMessageMain, mMessageThread;
pthread_mutex_t mWaitForStopMutex;
pthread_cond_t mWaitForStopCond;
bool mWaitForStop;


// eThread
void thread();
void thread_finished();

public:
ResolveUrl(const std::string &url);
~ResolveUrl();
void gotMessage(const Message &message);

void start();
void stop();
std::string getUrl();

void scriptEnded(int retval);
#if SIGCXX_MAJOR_VERSION == 2
sigc::signal1<void,int> urlResolved;
#else
Signal1<void, int> urlResolved;
#endif
};

#endif // SCRIPTRUN_H
40 changes: 36 additions & 4 deletions src/serviceapp/serviceapp.cpp
Expand Up @@ -208,6 +208,9 @@ eServiceApp::eServiceApp(eServiceReference ref):
m_subservices_checked(false),
player(0),
extplayer(0),
m_resolver(0),
m_resolve_uri("resolve://"),
m_event_started(false),
m_paused(false),
m_framerate(-1),
m_width(-1),
Expand Down Expand Up @@ -242,6 +245,7 @@ eServiceApp::~eServiceApp()
delete options;
delete player;
delete extplayer;
delete m_resolver;

if (m_subtitle_widget) m_subtitle_widget->destroy();
m_subtitle_widget = 0;
Expand Down Expand Up @@ -570,7 +574,20 @@ void eServiceApp::pushSubtitles()
void eServiceApp::signalEventUpdatedInfo()
{
eDebug("eServiceApp::signalEventUpdatedInfo");
m_event(this, evUpdatedInfo);
m_event(this, evUpdatedInfo);
}

void eServiceApp::urlResolved(int success)
{
eDebug("eServiceApp::urlResolved: %s", success ? "success": "error");
if (success)
{
m_ref.path = m_resolver->getUrl();
eDebug("eServiceApp::urlResolved: %s", m_ref.path.c_str());
start();
}
else
stop();
}

void eServiceApp::gotExtPlayerMessage(int message)
Expand All @@ -579,8 +596,6 @@ void eServiceApp::gotExtPlayerMessage(int message)
{
case PlayerMessage::start:
eDebug("eServiceApp::gotExtPlayerMessage - start");
m_event(this, evUpdatedEventInfo);
m_event(this, evStart);
m_event_updated_info_timer->start(1000, true);
#ifdef HAVE_EPG
updateEpgCacheNowNext();
Expand Down Expand Up @@ -667,13 +682,28 @@ RESULT eServiceApp::connectEvent(const Slot2< void, iPlayableService*, int >& ev

RESULT eServiceApp::start()
{
if (!m_event_started)
{
m_event(this, evUpdatedEventInfo);
m_event(this, evStart);
m_event_started = true;
}
std::string path_str(m_ref.path);

if (path_str.find(m_resolve_uri) == 0)
{
m_resolver = new ResolveUrl(m_ref.path.substr(m_resolve_uri.size()));
CONNECT(m_resolver->urlResolved, eServiceApp::urlResolved);
m_resolver->start();
return 0;
}
HeaderMap headers = getHttpHeaders(m_ref.path);
if (options->HLSExplorer && options->autoSelectStream)
{
if (!m_subservices_checked)
{
fillSubservices();
m_event(this, evUpdatedEventInfo);
m_subservices_checked = true;
}
size_t subservice_num = m_subservice_vec.size();
Expand Down Expand Up @@ -734,6 +764,7 @@ RESULT eServiceApp::start()
RESULT eServiceApp::stop()
{
eDebug("eServiceApp::stop");
if (m_resolver) m_resolver->stop();
player->stop();
return 0;
}
Expand Down Expand Up @@ -1090,7 +1121,8 @@ RESULT eServiceApp::getSubtitleList(std::vector<struct SubtitleTrack> &subtitlel
// __iSubservices
int eServiceApp::getNumberOfSubservices()
{
if (options->HLSExplorer && !m_subservices_checked)
std::string path_str(m_ref.path);
if (options->HLSExplorer && path_str.find(m_resolve_uri) && !m_subservices_checked)
{
fillSubservices();
m_subservices_checked = true;
Expand Down

0 comments on commit e461ca6

Please sign in to comment.