diff options
| author | Antti Ajanki <antti.ajanki@iki.fi> | 2013-08-08 17:16:00 +0300 | 
|---|---|---|
| committer | Antti Ajanki <antti.ajanki@iki.fi> | 2013-08-08 17:16:00 +0300 | 
| commit | b3bdb5e6a1515c938fec0661bb56f4b39538195c (patch) | |
| tree | 874355a1601ed713a8e4dc4e78806862f369fd63 /src | |
| parent | 78e8b44d32231a45086fe3c7e1465e8bb60b871c (diff) | |
| download | vdr-plugin-webvideo-b3bdb5e6a1515c938fec0661bb56f4b39538195c.tar.gz vdr-plugin-webvideo-b3bdb5e6a1515c938fec0661bb56f4b39538195c.tar.bz2 | |
Combine quvi and external downloader pipes into a pipe that reads a
menu written by an external shell script (src/menuscripts). Add YLE
Areena as an example script (TODO: downloading rtmp streams).
Diffstat (limited to 'src')
| -rw-r--r-- | src/libwebvi/libwebvi.c | 4 | ||||
| -rw-r--r-- | src/libwebvi/libwebvi.h | 6 | ||||
| -rw-r--r-- | src/libwebvi/link.c | 2 | ||||
| -rw-r--r-- | src/libwebvi/link.h | 2 | ||||
| -rw-r--r-- | src/libwebvi/linktemplates.c | 9 | ||||
| -rw-r--r-- | src/libwebvi/menubuilder.c | 2 | ||||
| -rw-r--r-- | src/libwebvi/pipecomponent.c | 223 | ||||
| -rw-r--r-- | src/libwebvi/pipecomponent.h | 7 | ||||
| -rw-r--r-- | src/libwebvi/request.c | 28 | ||||
| -rw-r--r-- | src/libwebvi/webvicontext.c | 15 | ||||
| -rw-r--r-- | src/libwebvi/webvicontext.h | 3 | ||||
| -rwxr-xr-x | src/menuscripts/stream-areena | 14 | ||||
| -rwxr-xr-x | src/menuscripts/stream-quvi | 19 | ||||
| -rwxr-xr-x | src/menuscripts/utils.sh | 15 | ||||
| -rw-r--r-- | src/pywebvi/pywebvi.py | 10 | ||||
| -rw-r--r-- | src/webvicli/webvicli/client.py | 22 | 
16 files changed, 212 insertions, 169 deletions
| diff --git a/src/libwebvi/libwebvi.c b/src/libwebvi/libwebvi.c index feeec28..cd6df13 100644 --- a/src/libwebvi/libwebvi.c +++ b/src/libwebvi/libwebvi.c @@ -101,6 +101,10 @@ WebviResult webvi_set_config(WebviCtx ctxhandle, WebviConfig conf, ...) {      webvi_context_set_timeout_data(ctx, data);      break;    } +  case WEBVI_CONFIG_MENU_SCRIPT_PATH: +    p = va_arg(argptr, char *); +    webvi_context_set_menu_script_path(ctx, p); +    break;    default:      res = WEBVIERR_INVALID_PARAMETER;    }; diff --git a/src/libwebvi/libwebvi.h b/src/libwebvi/libwebvi.h index 8efe953..74c9f6c 100644 --- a/src/libwebvi/libwebvi.h +++ b/src/libwebvi/libwebvi.h @@ -102,7 +102,8 @@ typedef enum {    WEBVI_CONFIG_TEMPLATE_PATH,    WEBVI_CONFIG_DEBUG,    WEBVI_CONFIG_TIMEOUT_CALLBACK, -  WEBVI_CONFIG_TIMEOUT_DATA +  WEBVI_CONFIG_TIMEOUT_DATA, +  WEBVI_CONFIG_MENU_SCRIPT_PATH  } WebviConfig;  typedef struct { @@ -167,6 +168,9 @@ LIBWEBVI_DLL_EXPORT const char* webvi_strerror(WebviResult err);   * WEBVI_CONFIG_TEMPLATE_PATH   *   Set the base directory for the XSLT templates (char *)   * + * WEBVI_CONFIG_MENU_SCRIPT_PATH + *   Specify the directory where to look for the menu scripts (char *) + *   * WEBVI_CONFIG_DEBUG   *   If value is not "0", print debug output to stdin (char *)   * diff --git a/src/libwebvi/link.c b/src/libwebvi/link.c index 501e70a..fbce59b 100644 --- a/src/libwebvi/link.c +++ b/src/libwebvi/link.c @@ -73,7 +73,7 @@ struct ActionTypeMessage {  const char *link_action_type_to_string(LinkActionType atype) {    static struct ActionTypeMessage messages[] =       {{LINK_ACTION_PARSE, "regular link"}, -     {LINK_ACTION_STREAM_LIBQUVI, "stream"}, +     {LINK_ACTION_STREAM, "stream"},       {LINK_ACTION_EXTERNAL_COMMAND, "external command"}};    for (int i=0;  i<(sizeof(messages)/sizeof(messages[0])); i++) { diff --git a/src/libwebvi/link.h b/src/libwebvi/link.h index 0ddc05c..9fac1f4 100644 --- a/src/libwebvi/link.h +++ b/src/libwebvi/link.h @@ -22,7 +22,7 @@  typedef enum {    LINK_ACTION_PARSE, -  LINK_ACTION_STREAM_LIBQUVI, +  LINK_ACTION_STREAM,    LINK_ACTION_EXTERNAL_COMMAND  } LinkActionType; diff --git a/src/libwebvi/linktemplates.c b/src/libwebvi/linktemplates.c index a193df3..e7f76fd 100644 --- a/src/libwebvi/linktemplates.c +++ b/src/libwebvi/linktemplates.c @@ -126,15 +126,12 @@ LinkAction *parse_action(const gchar *actionstr) {    if (*actionstr == '\0') {      return link_action_create(LINK_ACTION_PARSE, NULL); -  } else if (strcmp(actionstr, STREAM_LIBQUVI_SELECTOR) == 0) { -    return link_action_create(LINK_ACTION_STREAM_LIBQUVI, NULL); +  } else if (strncmp(actionstr, STREAM_SELECTOR, STREAM_SELECTOR_LEN) == 0) { +    const gchar *command = actionstr + STREAM_SELECTOR_LEN; +    return link_action_create(LINK_ACTION_STREAM, command);    } else if (strncmp(actionstr, EXT_CMD_SELECTOR, EXT_CMD_SELECTOR_LEN) == 0) {      const gchar *command = actionstr + EXT_CMD_SELECTOR_LEN;      return link_action_create(LINK_ACTION_EXTERNAL_COMMAND, command); -  } else if (strncmp(actionstr, STREAM_SELECTOR, STREAM_SELECTOR_LEN) == 0) { -    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_WARNING, -          "Unknown streamer %s in link template file", actionstr); -    return NULL;    } else {      g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_WARNING,            "Invalid action %s in link template file", actionstr); diff --git a/src/libwebvi/menubuilder.c b/src/libwebvi/menubuilder.c index d2b54ce..bffbc1e 100644 --- a/src/libwebvi/menubuilder.c +++ b/src/libwebvi/menubuilder.c @@ -67,7 +67,7 @@ void add_link_to_menu(gpointer data, gpointer instance) {  void menu_builder_append_link(MenuBuilder *self, const Link *link) {    const char *class; -  if (link_get_type(link) == LINK_ACTION_STREAM_LIBQUVI) { +  if (link_get_type(link) == LINK_ACTION_STREAM) {      class = "stream";    } else {      class = "webvi"; diff --git a/src/libwebvi/pipecomponent.c b/src/libwebvi/pipecomponent.c index 34e7634..7390bf3 100644 --- a/src/libwebvi/pipecomponent.c +++ b/src/libwebvi/pipecomponent.c @@ -52,8 +52,11 @@ struct PipeMainMenuDownloader {  struct PipeExternalDownloader {    PipeComponent pipe_data; +  gchar *command;    gchar *url; -  int fd; +  gchar *menu_script_path; +  GPid pid; +  gint fd;  };  struct PipeLocalFile { @@ -62,11 +65,8 @@ struct PipeLocalFile {    int fd;  }; -struct PipeLibquvi { +struct PipeMenuValidator {    PipeComponent pipe_data; -  gchar *url; -  GPid pid; -  gint quvi_output;    xmlParserCtxtPtr parser;  }; @@ -108,15 +108,9 @@ static gboolean pipe_external_downloader_handle_socket(    PipeComponent *instance, int fd, int bitmask);  static void pipe_external_downloader_delete(PipeComponent *instance); -static void pipe_libquvi_fdset(PipeComponent *instance, fd_set *readfd, -                               fd_set *writefd, fd_set *excfd, int *maxfd); -static gboolean pipe_libquvi_handle_socket(PipeComponent *instance, -                                           int fd, int bitmask); -static gboolean pipe_libquvi_parse(PipeComponent *instance, char *buf, size_t len); -static void pipe_libquvi_finished(PipeComponent *instance, RequestState state); -static xmlChar *quvi_xml_get_stream_url(xmlDoc *doc); -static xmlChar *quvi_xml_get_stream_title(xmlDoc *doc); -static void pipe_libquvi_delete(PipeComponent *instance); +static gboolean pipe_menu_validator_parse(PipeComponent *instance, char *buf, size_t len); +static void pipe_menu_validator_validate(PipeComponent *instance, RequestState state); +static void pipe_menu_validator_delete(PipeComponent *instance);  static CURL *start_curl(const char *url, CURLM *curlmulti,                          PipeComponent *instance); @@ -173,7 +167,7 @@ void pipe_component_finished(PipeComponent *self, RequestState state) {      if (self->finished)        self->finished(self, state);      if (self->next) -      pipe_component_finished(self->next, state); +      pipe_component_finished(self->next, self->state);    }  } @@ -470,14 +464,16 @@ void pipe_local_file_delete(PipeComponent *instance) {  /***** PipeExternalDownloader *****/ -PipeExternalDownloader *pipe_external_downloader_create(const gchar *url, -                                                        const gchar *command) +PipeExternalDownloader *pipe_external_downloader_create( +  const gchar *url, const gchar *command, const gchar *menu_script_path)  {    INITIALIZE_PIPE_WITH_FDSET(PipeExternalDownloader, NULL, NULL,                               pipe_external_downloader_delete,                               pipe_external_downloader_fdset,                               pipe_external_downloader_handle_socket); +  self->command = g_strdup(command);    self->url = g_strdup(url); +  self->menu_script_path = g_strdup(menu_script_path);    self->fd = -1;    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, @@ -487,8 +483,21 @@ PipeExternalDownloader *pipe_external_downloader_create(const gchar *url,  }  void pipe_external_downloader_start(PipeExternalDownloader *self) { -  // FIXME -  pipe_component_finished((PipeComponent *)self, WEBVISTATE_INTERNAL_ERROR); +  GError *error = NULL; +  gchar *argv[] = {self->command, self->url, NULL}; +  /* gchar *envpath = g_strconcat("PATH=", LIBWEBVI_SCRIPT_PATH, */ +  /*                              ":/usr/bin:/bin:/usr/local/bin", NULL); */ +  /* gchar *envp[] = {envpath, NULL}; */ +  g_spawn_async_with_pipes(self->menu_script_path, argv, NULL, 0, +                           NULL, NULL, &self->pid, NULL, &self->fd, +                           NULL, &error); +  if (error) { +    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_INFO, +          "Executing '%s %s' failed: %s", self->command, self->url, +          error->message); +    pipe_component_finished((PipeComponent *)self, WEBVISTATE_SUBPROCESS_FAILED); +  } +  /* g_free(envpath); */  }  void pipe_external_downloader_fdset(PipeComponent *instance, fd_set *readfd, @@ -507,164 +516,102 @@ gboolean pipe_external_downloader_handle_socket(PipeComponent *instance,  void pipe_external_downloader_delete(PipeComponent *instance) {    PipeExternalDownloader *self = (PipeExternalDownloader *)instance; -  if (self->fd != -1) +  if (self->fd != -1) {      close(self->fd); +  } +  if (self->pid != -1) { +    g_spawn_close_pid(self->pid); +  }    g_free(self->url); -  g_free(self); +  g_free(self->command); +  g_free(self->menu_script_path); +  free(self);  } -/***** PipeLibquvi *****/ +/***** PipeMenuValidator *****/ -PipeLibquvi *pipe_libquvi_create(const gchar *url) { -  INITIALIZE_PIPE_WITH_FDSET(PipeLibquvi, -                             pipe_libquvi_parse, -                             pipe_libquvi_finished, -                             pipe_libquvi_delete, -                             pipe_libquvi_fdset, -                             pipe_libquvi_handle_socket); -  self->url = g_strdup(url); -  self->pid = -1; -  self->quvi_output = -1; +PipeMenuValidator *pipe_menu_validator_create() { +  INITIALIZE_PIPE(PipeMenuValidator, pipe_menu_validator_parse, +                  pipe_menu_validator_validate, +                  pipe_menu_validator_delete);    return self;  } -void pipe_libquvi_start(PipeLibquvi *self) { -  GError *error = NULL; -  gchar *argv[] = {"quvi", "--xml", self->url}; - -  g_spawn_async_with_pipes(NULL, argv, NULL, -                           G_SPAWN_SEARCH_PATH | G_SPAWN_STDERR_TO_DEV_NULL, -                           NULL, NULL, &self->pid, NULL, &self->quvi_output, -                           NULL, &error); -  if (error) { -    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_WARNING, -          "Calling quvi failed: %s", error->message); -    pipe_component_finished((PipeComponent *)self, WEBVISTATE_SUBPROCESS_FAILED); -  } -} - -void pipe_libquvi_fdset(PipeComponent *instance, fd_set *readfd, -                        fd_set *writefd, fd_set *excfd, int *maxfd) -{ -  PipeLibquvi *self = (PipeLibquvi *)instance; -  append_to_fdset(self->quvi_output, readfd, maxfd); -} - -gboolean pipe_libquvi_handle_socket(PipeComponent *instance, -                                    int fd, int bitmask) -{ -  PipeLibquvi *self = (PipeLibquvi *)instance; -  return read_from_fd_to_pipe(instance, &self->quvi_output, fd, bitmask); -} - -gboolean pipe_libquvi_parse(PipeComponent *instance, char *buf, size_t len) { -  PipeLibquvi *self = (PipeLibquvi *)instance; +gboolean pipe_menu_validator_parse(PipeComponent *instance, char *buf, size_t len) { +  PipeMenuValidator *self = (PipeMenuValidator *)instance;    if (!self->parser) { -    self->parser = xmlCreatePushParserCtxt(NULL, NULL, buf, len, "quvioutput.xml"); -    g_assert(self->parser); +    self->parser = xmlCreatePushParserCtxt(NULL, NULL, buf, len, +                                           "externalscript.xml"); +    if (!self->parser) { +      pipe_component_finished(instance, WEBVISTATE_MEMORY_ALLOCATION_ERROR); +    }    } else { -    xmlParseChunk(self->parser, buf, len, 0); +    int err = xmlParseChunk(self->parser, buf, len, 0); +    if (err) { +      g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_INFO, +            "Menu is not valid XML (libxml2 error %d)", err); +      pipe_component_finished(instance, WEBVISTATE_SUBPROCESS_FAILED); +      return FALSE; +    }    } - -  return FALSE; +  return TRUE;  } -void pipe_libquvi_finished(PipeComponent *instance, RequestState state) { +void pipe_menu_validator_validate(PipeComponent *instance, RequestState state) { +  PipeMenuValidator *self = (PipeMenuValidator *)instance;    if (state != WEBVISTATE_FINISHED_OK) { -    pipe_component_finished(instance, state);      return;    } -  PipeLibquvi *self = (PipeLibquvi *)instance;    if (!self->parser) { -    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "No output from quvi!"); -    pipe_component_finished(instance, WEBVISTATE_SUBPROCESS_FAILED); +    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "no menu to validate"); +    return; +  } + +  int err = xmlParseChunk(self->parser, NULL, 0, 1); +  if (err) { +    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_INFO, +          "Menu is not valid XML (xmlParserErrors %d)", err); +    instance->state = WEBVISTATE_SUBPROCESS_FAILED;      return;    } -  xmlParseChunk(self->parser, NULL, 0, 1);    xmlDoc *doc = self->parser->myDoc; +  if (!doc) { +    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_INFO, +          "XML parser returned no document"); +    instance->state = WEBVISTATE_SUBPROCESS_FAILED; +    return; +  }    xmlChar *dump;    int dumpLen;    xmlDocDumpMemory(doc, &dump, &dumpLen); -  g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "quvi output:\n%s", dump); +  g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "validating menu:\n%s", dump);    xmlFree(dump);    dump = NULL; -  xmlChar *encoded_url = quvi_xml_get_stream_url(doc); -  if (!encoded_url) { -    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "No url in quvi output!"); -    pipe_component_finished(instance, WEBVISTATE_SUBPROCESS_FAILED); -    return; -  } - -  /* URLs in quvi XML output are encoded by curl_easy_escape */ -  char *url = curl_unescape((char *)encoded_url, strlen((char *)encoded_url)); -  xmlChar *title = quvi_xml_get_stream_title(doc); - -  MenuBuilder *menu_builder = menu_builder_create(); -  menu_builder_set_title(menu_builder, (char *)title); -  menu_builder_append_link_plain(menu_builder, url, (char *)title, NULL); -  char *menu = menu_builder_to_string(menu_builder); -  if (self->pipe_data.next) { -    pipe_component_append(self->pipe_data.next, menu, strlen(menu)); -    pipe_component_finished(self->pipe_data.next, state); -  } - -  free(menu); -  menu_builder_delete(menu_builder); -  xmlFree(title); -  curl_free(url); -  xmlFree(encoded_url); -} - -xmlChar *quvi_xml_get_stream_url(xmlDoc *doc) {    xmlNode *root = xmlDocGetRootElement(doc); -  xmlNode *node = root->children; -  while (node) { -    if (xmlStrEqual(node->name, BAD_CAST "link")) { -      xmlNode *link_child = node->children; -      while (link_child) { -        if (xmlStrEqual(link_child->name, BAD_CAST "url")) { -          return xmlNodeGetContent(link_child); -        } -        link_child = link_child->next; -      } -    } -    node = node->next; -  } - -  return NULL; -} - -xmlChar *quvi_xml_get_stream_title(xmlDoc *doc) { -  xmlNode *root = xmlDocGetRootElement(doc); -  xmlNode *node = root->children; -  while (node) { -    if (xmlStrEqual(node->name, BAD_CAST "page_title")) { -      return xmlNodeGetContent(node); -    } -    node = node->next; +  if (!xmlStrEqual(root->name, BAD_CAST "wvmenu")) { +    g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_INFO, +          "Unexpected root node: %s", (char *)root->name); +    instance->state = WEBVISTATE_SUBPROCESS_FAILED; +    return;    } -  return NULL; +  g_log(LIBWEBVI_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Menu is valid");  } -void pipe_libquvi_delete(PipeComponent *instance) { -  PipeLibquvi *self = (PipeLibquvi *)instance; -  if (self->quvi_output != -1) { -    close(self->quvi_output); -  } -  if (self->pid != -1) { -    g_spawn_close_pid(self->pid); -  } +void pipe_menu_validator_delete(PipeComponent *instance) { +  PipeMenuValidator *self = (PipeMenuValidator *)instance;    if (self->parser) { +    if (self->parser->myDoc) { +      xmlFreeDoc(self->parser->myDoc); +    }      xmlFreeParserCtxt(self->parser);    } -  g_free(self->url);    free(self);  } diff --git a/src/libwebvi/pipecomponent.h b/src/libwebvi/pipecomponent.h index 00a05ef..451cc96 100644 --- a/src/libwebvi/pipecomponent.h +++ b/src/libwebvi/pipecomponent.h @@ -47,6 +47,7 @@ typedef struct PipeMainMenuDownloader PipeMainMenuDownloader;  typedef struct PipeLocalFile PipeLocalFile;  typedef struct PipeExternalDownloader PipeExternalDownloader;  typedef struct PipeLibquvi PipeLibquvi; +typedef struct PipeMenuValidator PipeMenuValidator;  void pipe_component_initialize(PipeComponent *self,      gboolean (*process_cb)(PipeComponent *, char *, size_t), @@ -85,11 +86,13 @@ PipeCallbackWrapper *pipe_callback_wrapper_create(      void (*finish_callback)(RequestState, void *),      void *finishdata); -PipeExternalDownloader *pipe_external_downloader_create(const gchar *url, -                                                        const gchar *command); +PipeExternalDownloader *pipe_external_downloader_create( +  const gchar *url, const gchar *command, const gchar *menu_script_path);  void pipe_external_downloader_start(PipeExternalDownloader *self);  PipeLibquvi *pipe_libquvi_create(const gchar *url);  void pipe_libquvi_start(PipeLibquvi *self); +PipeMenuValidator *pipe_menu_validator_create(); +  #endif // __PIPECOMPONENT_H diff --git a/src/libwebvi/request.c b/src/libwebvi/request.c index af078c4..fc2b635 100644 --- a/src/libwebvi/request.c +++ b/src/libwebvi/request.c @@ -28,7 +28,6 @@ static PipeComponent *build_and_start_mainmenu_pipe(const WebviRequest *self);  static PipeComponent *build_and_start_local_pipe(const WebviRequest *self);  static PipeComponent *build_and_start_external_pipe(const WebviRequest *self,                                                      const char *command); -static PipeComponent *build_and_start_libquvi_pipe(const WebviRequest *self);  static PipeComponent *build_and_start_menu_pipe(const WebviRequest *self);  WebviRequest *request_create(const char *url, struct WebviContext *ctx) { @@ -73,9 +72,8 @@ PipeComponent *pipe_factory(const WebviRequest *self) {      const LinkAction *action = link_templates_get_action(        get_link_templates(self->ctx), self->url);      LinkActionType action_type = action ? link_action_get_type(action) : LINK_ACTION_PARSE; -    if (action_type == LINK_ACTION_STREAM_LIBQUVI) { -      head = build_and_start_libquvi_pipe(self); -    } else if (action_type == LINK_ACTION_EXTERNAL_COMMAND) { +    if ((action_type == LINK_ACTION_STREAM) ||  +        (action_type == LINK_ACTION_EXTERNAL_COMMAND)) {        const char *command = link_action_get_command(action);        head = build_and_start_external_pipe(self, command);      } else { @@ -108,26 +106,22 @@ PipeComponent *build_and_start_local_pipe(const WebviRequest *self) {    return (PipeComponent *)p1;  } -PipeComponent *build_and_start_external_pipe(const WebviRequest *self, const char *command) { -  PipeExternalDownloader *p1 = pipe_external_downloader_create(self->url, command); -  PipeComponent *p2 = (PipeComponent *)pipe_callback_wrapper_create( +PipeComponent *build_and_start_external_pipe(const WebviRequest *self, +                                             const char *command) +{ +  const char *path = webvi_context_get_menu_script_path(self->ctx); +  PipeExternalDownloader *p1 = pipe_external_downloader_create( +    self->url, command, path); +  PipeComponent *p2 = (PipeComponent *)pipe_menu_validator_create(); +  PipeComponent *p3 = (PipeComponent *)pipe_callback_wrapper_create(      self->read_cb, self->readdata, notify_pipe_finished, (void *)self);    pipe_component_set_next((PipeComponent *)p1, p2); +  pipe_component_set_next((PipeComponent *)p2, p3);    pipe_external_downloader_start(p1);    return (PipeComponent *)p1;  } -PipeComponent *build_and_start_libquvi_pipe(const WebviRequest *self) { -  PipeLibquvi *p1 = pipe_libquvi_create(self->url); -  PipeComponent *p2 = (PipeComponent *)pipe_callback_wrapper_create( -    self->read_cb, self->readdata, notify_pipe_finished, (void *)self); -  pipe_component_set_next((PipeComponent *)p1, p2); -  pipe_libquvi_start(p1); - -  return (PipeComponent *)p1; -} -  PipeComponent *build_and_start_menu_pipe(const WebviRequest *self) {    CURLM *curlmulti = webvi_context_get_curl_multi_handle(self->ctx);    PipeDownloader *p1 = pipe_downloader_create(self->url, curlmulti); diff --git a/src/libwebvi/webvicontext.c b/src/libwebvi/webvicontext.c index 16cba10..c6c43a4 100644 --- a/src/libwebvi/webvicontext.c +++ b/src/libwebvi/webvicontext.c @@ -6,6 +6,7 @@  #include "request.h"  #define DEFAULT_TEMPLATE_PATH "/etc/webvi/websites" +#define DEFAULT_MENU_SCRIPT_PATH "/usr/local/bin/webvi/menuscripts"  #define MAX_MESSAGE_LENGTH 128  struct WebviContext { @@ -14,6 +15,7 @@ struct WebviContext {    WebviHandle next_request;    CURLM *curl_multi_handle;    gchar *template_path; +  gchar *menu_script_path;    webvi_timeout_callback timeout_callback;    void *timeout_data;    GArray *finish_messages; @@ -128,13 +130,24 @@ void webvi_context_set_template_path(WebviContext *self, const char *path) {    if (self->template_path) {      g_free(self->template_path);    } -  self->template_path = path ? g_strdup(path) : NULL; +  self->template_path = g_strdup(path);  }  const char *webvi_context_get_template_path(const WebviContext *self) {    return self->template_path ? self->template_path : DEFAULT_TEMPLATE_PATH;  } +void webvi_context_set_menu_script_path(WebviContext *self, const char *path) { +  if (self->menu_script_path) { +    g_free(self->menu_script_path); +  } +  self->menu_script_path = g_strdup(path); +} + +const char *webvi_context_get_menu_script_path(const WebviContext *self) { +  return self->menu_script_path ? self->menu_script_path : DEFAULT_MENU_SCRIPT_PATH; +} +  const LinkTemplates *get_link_templates(WebviContext *self) {    if (!self->link_templates) {      self->link_templates = link_templates_create(); diff --git a/src/libwebvi/webvicontext.h b/src/libwebvi/webvicontext.h index b3beb0e..210d940 100644 --- a/src/libwebvi/webvicontext.h +++ b/src/libwebvi/webvicontext.h @@ -41,6 +41,9 @@ void webvi_context_set_debug(WebviContext *self,  void webvi_context_set_template_path(WebviContext *self,                                       const char *path);  const char *webvi_context_get_template_path(const WebviContext *self); +void webvi_context_set_menu_script_path(WebviContext *self, +                                       const char *path); +const char *webvi_context_get_menu_script_path(const WebviContext *self);  const LinkTemplates *get_link_templates(WebviContext *self);  CURLM *webvi_context_get_curl_multi_handle(WebviContext *self);  void webvi_context_set_timeout_callback(WebviContext *ctx, diff --git a/src/menuscripts/stream-areena b/src/menuscripts/stream-areena new file mode 100755 index 0000000..7b0c41f --- /dev/null +++ b/src/menuscripts/stream-areena @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e + +. utils.sh + +if [ "x$1" = "x" ]; then +    exit 1 +fi + +STREAMURL=$(yle-dl --showurl "$1") +STREAMTITLE=$(yle-dl --showtitle "$1") + +outputWebviMenu "$STREAMURL" "$STREAMTITLE" diff --git a/src/menuscripts/stream-quvi b/src/menuscripts/stream-quvi new file mode 100755 index 0000000..b1d7662 --- /dev/null +++ b/src/menuscripts/stream-quvi @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +. utils.sh + +if [ "x$1" = "x" ]; then +    exit 1 +fi + +Q=$(quvi "$1") +if [ "x$Q" = "x" ]; then +    exit 1 +fi + +STREAMURL=$(echo "$Q" | sed -n 's/.*"url": "\(.*\)".*/\1/p' | head -n1) +STREAMTITLE=$(echo "$Q" | sed -n 's/.*"page_title": "\(.*\)".*/\1/p' | head -n1) + +outputWebviMenu "$STREAMURL" "$STREAMTITLE" diff --git a/src/menuscripts/utils.sh b/src/menuscripts/utils.sh new file mode 100755 index 0000000..0be2324 --- /dev/null +++ b/src/menuscripts/utils.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +function XMLEscape() { +    echo "$1" | sed "s/\&/\&/g;s/>/\>/g;s/</\</g;s/'/\'/g" +} + +function outputWebviMenu() { +    local url=$(XMLEscape "$1") +    local title=$(XMLEscape "$2") +    echo '<?xml version="1.0" encoding="UTF-8"?>' +    echo -n '<wvmenu><title>'"$title"'</title>' +    echo -n '<ul><li>' +    echo -n '<a href="'"$url"'">'"$title"'</a>' +    echo '</li></ul></wvmenu>' +} diff --git a/src/pywebvi/pywebvi.py b/src/pywebvi/pywebvi.py index b3df416..3815d0f 100644 --- a/src/pywebvi/pywebvi.py +++ b/src/pywebvi/pywebvi.py @@ -20,6 +20,7 @@ _WEBVI_CONFIG_TEMPLATE_PATH = 0  _WEBVI_CONFIG_DEBUG = 1  _WEBVI_CONFIG_TIMEOUT_CALLBACK = 2  _WEBVI_CONFIG_TIMEOUT_DATA = 3 +_WEBVI_CONFIG_MENU_SCRIPT_PATH = 4  class WebviState:      NOT_FINISHED = 0 @@ -107,6 +108,15 @@ class WebviContext:          set_config.restype = raise_if_webvi_result_not_ok          set_config(self.handle, _WEBVI_CONFIG_TEMPLATE_PATH, path) +    def set_menu_script_path(self, path): +        if self.handle is None: +            return +         +        set_config = libwebvi.webvi_set_config +        set_config.argtypes = [c_long, c_int, c_char_p] +        set_config.restype = raise_if_webvi_result_not_ok +        set_config(self.handle, _WEBVI_CONFIG_MENU_SCRIPT_PATH, path) +      def set_debug(self, enabled):          if self.handle is None:              return diff --git a/src/webvicli/webvicli/client.py b/src/webvicli/webvicli/client.py index 0af470d..9247fa1 100644 --- a/src/webvicli/webvicli/client.py +++ b/src/webvicli/webvicli/client.py @@ -106,6 +106,11 @@ def next_available_file_name(basename, ext):          i += 1      return '%s-%d%s' % (basename, i, ext) +def is_rtmp_url(url): +    RTMP_SCHEMES = ['rtmp://', 'rtmpe://', 'rtmps://', +                    'rtmpt://', 'rtmpte://', 'rtmpts://'] +    return any(url.startswith(x) for x in RTMP_SCHEMES) +  class WebviURLopener(urllib.FancyURLopener):      version = WEBVI_STREAM_USER_AGENT @@ -242,7 +247,10 @@ class WVClient:      def set_template_path(self, path):          self.webvi.set_template_path(path) -         + +    def set_menu_script_path(self, path): +        self.webvi.set_menu_script_path(path) +      def update_timeout(self, timeout_ms):          if timeout_ms < 0:              self.alarm = None @@ -429,6 +437,10 @@ class WVClient:          return (menu[0].activate(), menu[0].label)      def download_stream(self, url, title): +        if is_rtmp_url(url): +            print 'FIXME: downloading RTMP stream' +            return +                  try:              (tmpfilename, headers) = \                urllib.urlretrieve(url, reporthook=dl_progress) @@ -757,6 +769,10 @@ def parse_command_line(cmdlineargs, options):                        dest='templatepath',                        help='read video site templates from DIR', metavar='DIR',                        default=None) +    parser.add_option('-m', '--menuscriptpath', type='string', +                      dest='menuscriptpath', +                      help='read menu scripts from DIR', metavar='DIR', +                      default=None)      parser.add_option('-v', '--verbose', action='store_const', const=1,                        dest='verbose', help='debug output', default=0)      parser.add_option('--vfat', action='store_true', @@ -770,6 +786,8 @@ def parse_command_line(cmdlineargs, options):      if cmdlineopt.templatepath is not None:          options['templatepath'] = cmdlineopt.templatepath +    if cmdlineopt.menuscriptpath is not None: +        options['menuscriptpath'] = cmdlineopt.menuscriptpath      if cmdlineopt.verbose > 0:          options['verbose'] = cmdlineopt.verbose      if cmdlineopt.vfat: @@ -815,6 +833,8 @@ def main(argv):          client.set_debug(options['verbose'])      if options.has_key('templatepath'):          client.set_template_path(options['templatepath']) +    if options.has_key('menuscriptpath'): +        client.set_menu_script_path(options['menuscriptpath'])      if options.has_key('url'):          stream = options['url'] | 
