Initial work for web-ui based xmltv configuration
This commit is contained in:
parent
1fb2447c31
commit
8161549c2f
13 changed files with 666 additions and 119 deletions
4
Makefile
4
Makefile
|
@ -3,7 +3,7 @@
|
|||
SRCS = main.c dispatch.c channels.c transports.c teletext.c psi.c \
|
||||
subscriptions.c mux.c tsdemux.c buffer.c tcp.c \
|
||||
resolver.c tsmux.c parsers.c bitstream.c parser_h264.c spawn.c \
|
||||
notify.c
|
||||
notify.c intercom.c
|
||||
|
||||
SRCS += http.c
|
||||
|
||||
|
@ -36,7 +36,7 @@ SRCS += FFdecsa.c
|
|||
VPATH += ajaxui
|
||||
SRCS += ajaxui.c ajaxui_mailbox.c ajaxui_channels.c \
|
||||
ajaxui_config.c ajaxui_config_channels.c ajaxui_config_dvb.c \
|
||||
ajaxui_config_transport.c
|
||||
ajaxui_config_transport.c ajaxui_config_xmltv.c
|
||||
|
||||
JSSRCS += tvheadend.js
|
||||
|
||||
|
|
|
@ -61,6 +61,10 @@ void ajax_config_channels_init(void);
|
|||
|
||||
int ajax_config_dvb_tab(http_connection_t *hc, http_reply_t *hr);
|
||||
void ajax_config_dvb_init(void);
|
||||
|
||||
int ajax_config_xmltv_tab(http_connection_t *hc, http_reply_t *hr);
|
||||
void ajax_config_xmltv_init(void);
|
||||
|
||||
void ajax_config_transport_init(void);
|
||||
|
||||
int ajax_transport_build_list(http_connection_t *hc, tcp_queue_t *tq,
|
||||
|
|
|
@ -31,11 +31,13 @@
|
|||
|
||||
#define AJAX_CONFIG_TAB_CHANNELS 0
|
||||
#define AJAX_CONFIG_TAB_DVB 1
|
||||
#define AJAX_CONFIG_TABS 2
|
||||
#define AJAX_CONFIG_TAB_XMLTV 2
|
||||
#define AJAX_CONFIG_TABS 3
|
||||
|
||||
const char *ajax_config_tabnames[] = {
|
||||
[AJAX_CONFIG_TAB_CHANNELS] = "Channels & Groups",
|
||||
[AJAX_CONFIG_TAB_DVB] = "DVB adapters",
|
||||
[AJAX_CONFIG_TAB_XMLTV] = "XML-TV",
|
||||
};
|
||||
|
||||
|
||||
|
@ -79,6 +81,8 @@ ajax_config_dispatch(http_connection_t *hc, http_reply_t *hr,
|
|||
return ajax_config_channels_tab(hc, hr);
|
||||
case AJAX_CONFIG_TAB_DVB:
|
||||
return ajax_config_dvb_tab(hc, hr);
|
||||
case AJAX_CONFIG_TAB_XMLTV:
|
||||
return ajax_config_xmltv_tab(hc, hr);
|
||||
|
||||
default:
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
|
@ -126,4 +130,5 @@ ajax_config_init(void)
|
|||
|
||||
ajax_config_channels_init();
|
||||
ajax_config_dvb_init();
|
||||
ajax_config_xmltv_init();
|
||||
}
|
||||
|
|
|
@ -676,7 +676,6 @@ ajax_dvbmuxeditor(http_connection_t *hc, http_reply_t *hr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
209
ajaxui/ajaxui_config_xmltv.c
Normal file
209
ajaxui/ajaxui_config_xmltv.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* tvheadend, AJAX / HTML user interface
|
||||
* Copyright (C) 2008 Andreas Öman
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "http.h"
|
||||
#include "ajaxui.h"
|
||||
#include "channels.h"
|
||||
#include "epg_xmltv.h"
|
||||
#include "psi.h"
|
||||
#include "transports.h"
|
||||
|
||||
#include "ajaxui_mailbox.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* XMLTV configuration
|
||||
*/
|
||||
int
|
||||
ajax_config_xmltv_tab(http_connection_t *hc, http_reply_t *hr)
|
||||
{
|
||||
tcp_queue_t *tq = &hr->hr_tq;
|
||||
xmltv_grabber_t *xg;
|
||||
int ngrabbers = 0;
|
||||
int displines = 21;
|
||||
int csize[10];
|
||||
const char *cells[10];
|
||||
int o = 1;
|
||||
char buf[200];
|
||||
|
||||
tcp_qprintf(tq, "<div style=\"overflow: auto; width: 100%\">");
|
||||
|
||||
switch(xmltv_status) {
|
||||
default:
|
||||
tcp_qprintf(tq, "<p style=\"text-align: center; font-weight: bold\">"
|
||||
"XMLTV subsystem is not yet fully initialized, please retry "
|
||||
"in a few seconds</p></div>");
|
||||
http_output_html(hc, hr);
|
||||
return 0;
|
||||
|
||||
case XMLTVSTATUS_FIND_GRABBERS_NOT_FOUND:
|
||||
tcp_qprintf(tq, "<p style=\"text-align: center; font-weight: bold\">"
|
||||
"XMLTV subsystem can not initialize</p>"
|
||||
"<p style=\"text-align: center\">"
|
||||
"Make sure that the 'tv_find_grabbers' executable is in "
|
||||
"the system path</p></div>");
|
||||
http_output_html(hc, hr);
|
||||
return 0;
|
||||
|
||||
case XMLTVSTATUS_RUNNING:
|
||||
break;
|
||||
}
|
||||
|
||||
tcp_qprintf(tq, "<div style=\"float: left; width:45%\">");
|
||||
|
||||
ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, "XMLTV grabbers");
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
|
||||
ngrabbers++;
|
||||
|
||||
ajax_table_header(hc, tq,
|
||||
(const char *[]){"Grabber", "Status", NULL},
|
||||
(int[]){4,2}, ngrabbers > displines, csize);
|
||||
|
||||
tcp_qprintf(tq, "<hr><div "
|
||||
"style=\"height: %dpx; overflow: auto\" class=\"normallist\">",
|
||||
MAX(displines, ngrabbers) * 14);
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link) {
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"<a href=\"javascript:void(0);\" "
|
||||
"onClick=\"new Ajax.Updater('grabberpane', "
|
||||
"'/ajax/xmltvgrabber/%s', {method: 'get', evalScripts: true})\""
|
||||
">%s</a>", xg->xg_identifier, xg->xg_title);
|
||||
|
||||
cells[0] = buf;
|
||||
cells[1] = xmltv_grabber_status(xg);
|
||||
cells[2] = NULL;
|
||||
|
||||
ajax_table_row(tq, cells, csize, &o,
|
||||
(const char *[]){NULL, "status"},
|
||||
xg->xg_identifier);
|
||||
|
||||
}
|
||||
ajax_mailbox_start(tq, "xmltvgrabbers");
|
||||
tcp_qprintf(tq, "</div>");
|
||||
|
||||
ajax_box_end(tq, AJAX_BOX_SIDEBOX);
|
||||
|
||||
tcp_qprintf(tq, "</div>"
|
||||
"<div id=\"grabberpane\" style=\"float: left; width:55%\">"
|
||||
"</div>");
|
||||
|
||||
tcp_qprintf(tq, "</div>");
|
||||
http_output_html(hc, hr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display detailes about a grabber
|
||||
*/
|
||||
static int
|
||||
ajax_xmltvgrabber(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
xmltv_grabber_t *xg;
|
||||
tcp_queue_t *tq = &hr->hr_tq;
|
||||
|
||||
|
||||
if(remain == NULL || (xg = xmltv_grabber_find(remain)) == NULL)
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, xg->xg_title);
|
||||
|
||||
tcp_qprintf(tq,"<div id=\"details_%s\">", xg->xg_identifier);
|
||||
|
||||
switch(xg->xg_status) {
|
||||
case XMLTV_GRABBER_DISABLED:
|
||||
tcp_qprintf(tq,
|
||||
"<p>This grabber is currently not enabled, click "
|
||||
"<a href=\"javascript:void(0);\" "
|
||||
"onClick=\"new Ajax.Request('/ajax/xmltvgrabbermode/%s', "
|
||||
"{parameters: {'mode': 'enable'}})\">here</a> "
|
||||
"to enable it</p>");
|
||||
break;
|
||||
|
||||
|
||||
case XMLTV_GRABBER_ENQUEUED:
|
||||
case XMLTV_GRABBER_GRABBING:
|
||||
case XMLTV_GRABBER_UNCONFIGURED:
|
||||
case XMLTV_GRABBER_DYSFUNCTIONAL:
|
||||
case XMLTV_GRABBER_IDLE:
|
||||
tcp_qprintf(tq, "<p>%s</p>", xmltv_grabber_status_long(xg, xg->xg_status));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
tcp_qprintf(tq,"</div>");
|
||||
|
||||
ajax_box_end(tq, AJAX_BOX_SIDEBOX);
|
||||
|
||||
ajax_mailbox_start(tq, xg->xg_identifier);
|
||||
http_output_html(hc, hr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enable / Disable a grabber
|
||||
*/
|
||||
static int
|
||||
ajax_xmltvgrabbermode(http_connection_t *hc, http_reply_t *hr,
|
||||
const char *remain, void *opaque)
|
||||
{
|
||||
xmltv_grabber_t *xg;
|
||||
tcp_queue_t *tq = &hr->hr_tq;
|
||||
|
||||
if(remain == NULL || (xg = xmltv_grabber_find(remain)) == NULL)
|
||||
return HTTP_STATUS_NOT_FOUND;
|
||||
|
||||
xmltv_grabber_enable(xg);
|
||||
|
||||
tcp_qprintf(tq,"$('details_%s').innerHTML='Ok, please wait...';",
|
||||
xg->xg_identifier);
|
||||
|
||||
http_output(hc, hr, "text/javascript; charset=UTF8", NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
ajax_config_xmltv_init(void)
|
||||
{
|
||||
http_path_add("/ajax/xmltvgrabber" , NULL, ajax_xmltvgrabber);
|
||||
http_path_add("/ajax/xmltvgrabbermode" , NULL, ajax_xmltvgrabbermode);
|
||||
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
#include "http.h"
|
||||
#include "ajaxui.h"
|
||||
#include "transports.h"
|
||||
#include "epg_xmltv.h"
|
||||
|
||||
#define MAILBOX_UNUSED_TIMEOUT 15
|
||||
#define MAILBOX_EMPTY_REPLY_TIMEOUT 10
|
||||
|
@ -407,7 +408,6 @@ ajax_mailbox_tdmi_services_change(th_dvb_mux_instance_t *tdmi)
|
|||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
ajax_mailbox_tda_change(th_dvb_adapter_t *tda)
|
||||
{
|
||||
|
@ -420,3 +420,18 @@ ajax_mailbox_tda_change(th_dvb_adapter_t *tda)
|
|||
"dvbmuxlist", tda->tda_identifier,
|
||||
buf);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ajax_mailbox_xmltv_grabber_status_change(xmltv_grabber_t *xg, int status)
|
||||
{
|
||||
ajax_mailbox_update_div("xmltvgrabbers",
|
||||
"status", xg->xg_identifier,
|
||||
xmltv_grabber_status(xg));
|
||||
|
||||
ajax_mailbox_update_div(xg->xg_identifier,
|
||||
"details", xg->xg_identifier,
|
||||
xmltv_grabber_status_long(xg, status));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,4 +35,9 @@ void ajax_mailbox_tda_change(th_dvb_adapter_t *tda);
|
|||
|
||||
void ajax_mailbox_start(tcp_queue_t *tq, const char *identifier);
|
||||
|
||||
struct xmltv_grabber;
|
||||
|
||||
void ajax_mailbox_xmltv_grabber_status_change(struct xmltv_grabber *xg,
|
||||
int status);
|
||||
|
||||
#endif /* AJAXUI_MAILBOX_H_ */
|
||||
|
|
430
epg_xmltv.c
430
epg_xmltv.c
|
@ -37,48 +37,29 @@
|
|||
#include "epg_xmltv.h"
|
||||
#include "refstr.h"
|
||||
#include "spawn.h"
|
||||
#include "intercom.h"
|
||||
#include "notify.h"
|
||||
|
||||
extern int xmltvreload;
|
||||
|
||||
LIST_HEAD(, xmltv_channel) xmltv_channel_list;
|
||||
|
||||
typedef struct xmltv_map {
|
||||
LIST_ENTRY(xmltv_map) xm_link;
|
||||
th_channel_t *xm_channel; /* Set if we have resolved the channel */
|
||||
|
||||
int xm_isupdated;
|
||||
|
||||
} xmltv_map_t;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct xmltv_channel {
|
||||
LIST_ENTRY(xmltv_channel) xc_link;
|
||||
const char *xc_name;
|
||||
const char *xc_displayname;
|
||||
refstr_t *xc_icon;
|
||||
|
||||
LIST_HEAD(, xmltv_map) xc_maps;
|
||||
|
||||
struct event_queue xc_events;
|
||||
|
||||
} xmltv_channel_t;
|
||||
int xmltv_status;
|
||||
struct xmltv_grabber_list xmltv_grabbers;
|
||||
struct xmltv_grabber_queue xmltv_grabber_workq;
|
||||
static pthread_mutex_t xmltv_work_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t xmltv_work_cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
|
||||
static xmltv_channel_t *
|
||||
xc_find(const char *name)
|
||||
xc_find(xmltv_grabber_t *xg, const char *name)
|
||||
{
|
||||
xmltv_channel_t *xc;
|
||||
|
||||
LIST_FOREACH(xc, &xmltv_channel_list, xc_link)
|
||||
LIST_FOREACH(xc, &xg->xg_channels, xc_link)
|
||||
if(!strcmp(xc->xc_name, name))
|
||||
return xc;
|
||||
|
||||
xc = calloc(1, sizeof(xmltv_channel_t));
|
||||
xc->xc_name = strdup(name);
|
||||
TAILQ_INIT(&xc->xc_events);
|
||||
LIST_INSERT_HEAD(&xmltv_channel_list, xc, xc_link);
|
||||
LIST_INSERT_HEAD(&xg->xg_channels, xc, xc_link);
|
||||
return xc;
|
||||
}
|
||||
|
||||
|
@ -88,12 +69,12 @@ xc_find(const char *name)
|
|||
#define XML_FOREACH(n) for(; (n) != NULL; (n) = (n)->next)
|
||||
|
||||
static void
|
||||
xmltv_parse_channel(xmlNode *n, char *chid)
|
||||
xmltv_parse_channel(xmltv_grabber_t *xg, xmlNode *n, char *chid)
|
||||
{
|
||||
char *t, *c;
|
||||
xmltv_channel_t *xc;
|
||||
|
||||
xc = xc_find(chid);
|
||||
xc = xc_find(xg, chid);
|
||||
|
||||
XML_FOREACH(n) {
|
||||
|
||||
|
@ -107,8 +88,8 @@ xmltv_parse_channel(xmlNode *n, char *chid)
|
|||
if(!strcmp((char *)n->name, "icon")) {
|
||||
t = (char *)xmlGetProp(n, (unsigned char *)"src");
|
||||
|
||||
refstr_free(xc->xc_icon);
|
||||
xc->xc_icon = refstr_alloc(t);
|
||||
free(xc->xc_icon_url);
|
||||
xc->xc_icon_url = strdup(t);
|
||||
xmlFree(t);
|
||||
}
|
||||
xmlFree(c);
|
||||
|
@ -146,7 +127,8 @@ str2time(char *str)
|
|||
|
||||
|
||||
static void
|
||||
xmltv_parse_programme(xmlNode *n, char *chid, char *startstr, char *stopstr)
|
||||
xmltv_parse_programme(xmltv_grabber_t *xg, xmlNode *n,
|
||||
char *chid, char *startstr, char *stopstr)
|
||||
{
|
||||
char *c;
|
||||
event_t *e;
|
||||
|
@ -154,7 +136,7 @@ xmltv_parse_programme(xmlNode *n, char *chid, char *startstr, char *stopstr)
|
|||
int duration;
|
||||
xmltv_channel_t *xc;
|
||||
|
||||
xc = xc_find(chid);
|
||||
xc = xc_find(xg, chid);
|
||||
start = str2time(startstr);
|
||||
stop = str2time(stopstr);
|
||||
|
||||
|
@ -179,7 +161,7 @@ xmltv_parse_programme(xmlNode *n, char *chid, char *startstr, char *stopstr)
|
|||
|
||||
|
||||
static void
|
||||
xmltv_parse_tv(xmlNode *n)
|
||||
xmltv_parse_tv(xmltv_grabber_t *xg, xmlNode *n)
|
||||
{
|
||||
char *chid;
|
||||
char *start;
|
||||
|
@ -193,7 +175,7 @@ xmltv_parse_tv(xmlNode *n)
|
|||
if(!strcmp((char *)n->name, "channel")) {
|
||||
chid = (char *)xmlGetProp(n, (unsigned char *)"id");
|
||||
if(chid != NULL) {
|
||||
xmltv_parse_channel(n->children, chid);
|
||||
xmltv_parse_channel(xg, n->children, chid);
|
||||
xmlFree(chid);
|
||||
}
|
||||
} else if(!strcmp((char *)n->name, "programme")) {
|
||||
|
@ -201,7 +183,7 @@ xmltv_parse_tv(xmlNode *n)
|
|||
start = (char *)xmlGetProp(n, (unsigned char *)"start");
|
||||
stop = (char *)xmlGetProp(n, (unsigned char *)"stop");
|
||||
|
||||
xmltv_parse_programme(n->children, chid, start, stop);
|
||||
xmltv_parse_programme(xg, n->children, chid, start, stop);
|
||||
|
||||
xmlFree(chid);
|
||||
xmlFree(start);
|
||||
|
@ -213,11 +195,11 @@ xmltv_parse_tv(xmlNode *n)
|
|||
|
||||
|
||||
static void
|
||||
xmltv_parse_root(xmlNode *n)
|
||||
xmltv_parse_root(xmltv_grabber_t *xg, xmlNode *n)
|
||||
{
|
||||
XML_FOREACH(n) {
|
||||
if(n->type == XML_ELEMENT_NODE && !strcmp((char *)n->name, "tv"))
|
||||
xmltv_parse_tv(n->children);
|
||||
xmltv_parse_tv(xg, n->children);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,66 +207,22 @@ xmltv_parse_root(xmlNode *n)
|
|||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
xmltv_flush(void)
|
||||
static void
|
||||
xmltv_flush_events(xmltv_grabber_t *xg)
|
||||
{
|
||||
xmltv_channel_t *xc;
|
||||
xmltv_map_t *xm;
|
||||
event_t *e;
|
||||
|
||||
LIST_FOREACH(xc, &xmltv_channel_list, xc_link) {
|
||||
LIST_FOREACH(xc, &xg->xg_channels, xc_link) {
|
||||
while((e = TAILQ_FIRST(&xc->xc_events)) != NULL) {
|
||||
TAILQ_REMOVE(&xc->xc_events, e, e_link);
|
||||
epg_event_free(e);
|
||||
}
|
||||
|
||||
while((xm = LIST_FIRST(&xc->xc_maps)) != NULL) {
|
||||
LIST_REMOVE(xm, xm_link);
|
||||
free(xm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void
|
||||
xmltv_load(void)
|
||||
{
|
||||
xmlDoc *doc = NULL;
|
||||
xmlNode *root_element = NULL;
|
||||
const char *prog;
|
||||
char *outbuf;
|
||||
int outlen;
|
||||
prog = config_get_str("xmltvgrabber", NULL);
|
||||
if(prog == NULL)
|
||||
return;
|
||||
|
||||
syslog(LOG_INFO, "xmltv: Starting grabber \"%s\"", prog);
|
||||
|
||||
outlen = spawn_and_store_stdout(prog, &outbuf);
|
||||
if(outlen < 1) {
|
||||
syslog(LOG_ERR, "xmltv: Cannot parse output from \"%s\"", prog);
|
||||
return;
|
||||
}
|
||||
|
||||
doc = xmlParseMemory(outbuf, outlen);
|
||||
if(doc == NULL) {
|
||||
syslog(LOG_ERR, "xmltv: Error while parsing output from \"%s\"", prog);
|
||||
free(outbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
xmltv_flush();
|
||||
|
||||
root_element = xmlDocGetRootElement(doc);
|
||||
xmltv_parse_root(root_element);
|
||||
xmlFreeDoc(doc);
|
||||
xmlCleanupParser();
|
||||
syslog(LOG_INFO, "xmltv: EPG sucessfully loaded and parsed");
|
||||
free(outbuf);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
@ -381,7 +319,158 @@ xmltv_transfer(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Execute grabber and parse result,
|
||||
*
|
||||
* Return nextstate
|
||||
*/
|
||||
static int
|
||||
xmltv_grab(xmltv_grabber_t *xg)
|
||||
{
|
||||
xmlDoc *doc = NULL;
|
||||
xmlNode *root_element;
|
||||
const char *prog;
|
||||
char *outbuf;
|
||||
int outlen;
|
||||
|
||||
prog = xg->xg_path;
|
||||
|
||||
syslog(LOG_INFO, "xmltv: Starting grabber \"%s\"", prog);
|
||||
|
||||
outlen = spawn_and_store_stdout(prog, NULL, &outbuf);
|
||||
if(outlen < 1) {
|
||||
syslog(LOG_ERR, "xmltv: No output from \"%s\"", prog);
|
||||
return XMLTV_GRABBER_UNCONFIGURED;
|
||||
}
|
||||
|
||||
doc = xmlParseMemory(outbuf, outlen);
|
||||
if(doc == NULL) {
|
||||
syslog(LOG_ERR, "xmltv: Error while parsing output from \"%s\"", prog);
|
||||
free(outbuf);
|
||||
return XMLTV_GRABBER_DYSFUNCTIONAL;
|
||||
}
|
||||
|
||||
xmltv_flush_events(xg);
|
||||
|
||||
root_element = xmlDocGetRootElement(doc);
|
||||
xmltv_parse_root(xg, root_element);
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
xmlCleanupParser();
|
||||
syslog(LOG_INFO, "xmltv: EPG sucessfully loaded and parsed");
|
||||
free(outbuf);
|
||||
return XMLTV_GRABBER_IDLE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct strtab grabberstatustab[] = {
|
||||
{ "Disabled", XMLTV_GRABBER_DISABLED },
|
||||
{ "Missing configuration", XMLTV_GRABBER_UNCONFIGURED },
|
||||
{ "Dysfunctional", XMLTV_GRABBER_DYSFUNCTIONAL },
|
||||
{ "Waiting for execution", XMLTV_GRABBER_ENQUEUED },
|
||||
{ "Grabbing", XMLTV_GRABBER_GRABBING },
|
||||
{ "Idle", XMLTV_GRABBER_IDLE },
|
||||
};
|
||||
|
||||
|
||||
const char *
|
||||
xmltv_grabber_status(xmltv_grabber_t *xg)
|
||||
{
|
||||
return val2str(xg->xg_status, grabberstatustab) ?: "Invalid";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
grabbercmp(xmltv_grabber_t *a, xmltv_grabber_t *b)
|
||||
{
|
||||
return strcasecmp(a->xg_title, b->xg_title);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enlist all active grabbers (for user to choose among)
|
||||
*/
|
||||
static int
|
||||
xmltv_find_grabbers(const char *prog)
|
||||
{
|
||||
char *outbuf;
|
||||
int outlen;
|
||||
char *s, *a, *b;
|
||||
xmltv_grabber_t *xg;
|
||||
int n = 0;
|
||||
char buf[40];
|
||||
|
||||
outlen = spawn_and_store_stdout(prog, NULL, &outbuf);
|
||||
if(outlen < 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = outbuf;
|
||||
|
||||
while(1) {
|
||||
a = s;
|
||||
|
||||
while(*s && *s != '|')
|
||||
s++;
|
||||
|
||||
if(!*s)
|
||||
break;
|
||||
*s++ = 0;
|
||||
|
||||
b = s;
|
||||
|
||||
while(*s > 31)
|
||||
s++;
|
||||
|
||||
if(!*s)
|
||||
break;
|
||||
*s++ = 0;
|
||||
|
||||
while(*s < 31 && *s > 0)
|
||||
s++;
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
|
||||
if(!strcmp(b, xg->xg_title))
|
||||
break;
|
||||
|
||||
if(xg != NULL)
|
||||
continue;
|
||||
|
||||
xg = calloc(1, sizeof(xmltv_grabber_t));
|
||||
xg->xg_path = a;
|
||||
xg->xg_title = b;
|
||||
|
||||
snprintf(buf, sizeof(buf), "xmlgrabber_%d", n++);
|
||||
xg->xg_identifier = strdup(buf);
|
||||
|
||||
LIST_INSERT_SORTED(&xmltv_grabbers, xg, xg_link, grabbercmp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
xmltv_thread_set_status(icom_t *ic, xmltv_grabber_t *xg, int s)
|
||||
{
|
||||
htsmsg_t *m = htsmsg_create();
|
||||
|
||||
xg->xg_status = s;
|
||||
|
||||
htsmsg_add_u32(m, "status", s);
|
||||
htsmsg_add_str(m, "identifier", xg->xg_identifier);
|
||||
icom_send_msg_from_thread(ic, m);
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -389,31 +478,172 @@ xmltv_transfer(void)
|
|||
static void *
|
||||
xmltv_thread(void *aux)
|
||||
{
|
||||
int sleeptime;
|
||||
icom_t *ic = aux;
|
||||
struct timespec ts;
|
||||
xmltv_grabber_t *xg;
|
||||
int r;
|
||||
time_t n;
|
||||
|
||||
/* Default to 12 hours */
|
||||
sleeptime = atoi(config_get_str("xmltvinterval", "43200"));
|
||||
/* Start by finding all available grabbers, we try with a few
|
||||
different locations */
|
||||
|
||||
if(xmltv_find_grabbers("/bin/tv_find_grabbers") &&
|
||||
xmltv_find_grabbers("/usr/bin/tv_find_grabbers") &&
|
||||
xmltv_find_grabbers("/usr/local/bin/tv_find_grabbers")) {
|
||||
xmltv_status = XMLTVSTATUS_FIND_GRABBERS_NOT_FOUND;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xmltv_status = XMLTVSTATUS_RUNNING;
|
||||
|
||||
pthread_mutex_lock(&xmltv_work_lock);
|
||||
|
||||
while(1) {
|
||||
xmltv_load();
|
||||
xmltv_transfer();
|
||||
sleep(sleeptime);
|
||||
|
||||
xg = TAILQ_FIRST(&xmltv_grabber_workq);
|
||||
if(xg != NULL) {
|
||||
xmltv_thread_set_status(ic, xg, XMLTV_GRABBER_GRABBING);
|
||||
TAILQ_REMOVE(&xmltv_grabber_workq, xg, xg_work_link);
|
||||
pthread_mutex_unlock(&xmltv_work_lock);
|
||||
r = xmltv_grab(xg);
|
||||
pthread_mutex_lock(&xmltv_work_lock);
|
||||
|
||||
xmltv_thread_set_status(ic, xg, r);
|
||||
|
||||
time(&n);
|
||||
if(r == XMLTV_GRABBER_IDLE) {
|
||||
/* Completed load */
|
||||
xg->xg_nextgrab = n + 3600;
|
||||
} else {
|
||||
xg->xg_nextgrab = n + 60;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
n = INT32_MAX;
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
|
||||
n = xg->xg_nextgrab ? MIN(n, xg->xg_nextgrab) : n;
|
||||
|
||||
ts.tv_sec = n;
|
||||
ts.tv_nsec = 0;
|
||||
|
||||
pthread_cond_timedwait(&xmltv_work_cond, &xmltv_work_lock, &ts);
|
||||
|
||||
time(&n);
|
||||
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link) {
|
||||
switch(xg->xg_status) {
|
||||
case XMLTV_GRABBER_GRABBING:
|
||||
abort();
|
||||
|
||||
case XMLTV_GRABBER_DISABLED:
|
||||
case XMLTV_GRABBER_ENQUEUED:
|
||||
break;
|
||||
|
||||
case XMLTV_GRABBER_UNCONFIGURED:
|
||||
case XMLTV_GRABBER_DYSFUNCTIONAL:
|
||||
case XMLTV_GRABBER_IDLE:
|
||||
if(xg->xg_nextgrab <= n + 1)
|
||||
TAILQ_INSERT_TAIL(&xmltv_grabber_workq, xg, xg_work_link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sleep(100000);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
xmltv_grabber_enable(xmltv_grabber_t *xg)
|
||||
{
|
||||
pthread_mutex_lock(&xmltv_work_lock);
|
||||
|
||||
if(xg->xg_status == XMLTV_GRABBER_DISABLED) {
|
||||
xg->xg_status = XMLTV_GRABBER_ENQUEUED;
|
||||
TAILQ_INSERT_TAIL(&xmltv_grabber_workq, xg, xg_work_link);
|
||||
pthread_cond_signal(&xmltv_work_cond);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&xmltv_work_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
icom_cb(void *opaque, htsmsg_t *m)
|
||||
{
|
||||
const char *s;
|
||||
uint32_t status;
|
||||
xmltv_grabber_t *xg;
|
||||
|
||||
if(!htsmsg_get_u32(m, "status", &status)) {
|
||||
s = htsmsg_get_str(m, "identifier");
|
||||
if(s != NULL && (xg = xmltv_grabber_find(s)) != NULL) {
|
||||
notify_xmltv_grabber_status_change(xg, status);
|
||||
}
|
||||
}
|
||||
htsmsg_destroy(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a grabber by its id
|
||||
*/
|
||||
xmltv_grabber_t *
|
||||
xmltv_grabber_find(const char *id)
|
||||
{
|
||||
xmltv_grabber_t *xg;
|
||||
LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
|
||||
if(!strcmp(id, xg->xg_identifier))
|
||||
return xg;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
xmltv_init(void)
|
||||
{
|
||||
pthread_t ptid;
|
||||
icom_t *ic = icom_create(icom_cb, NULL);
|
||||
|
||||
if(config_get_str("xmltvgrabber", NULL) == NULL) {
|
||||
syslog(LOG_INFO, "xmltv: No grabber configured, xmltv subsystem disabled");
|
||||
return;
|
||||
}
|
||||
TAILQ_INIT(&xmltv_grabber_workq);
|
||||
|
||||
pthread_create(&ptid, NULL, xmltv_thread, NULL);
|
||||
pthread_create(&ptid, NULL, xmltv_thread, ic);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const char *
|
||||
xmltv_grabber_status_long(xmltv_grabber_t *xg, int status)
|
||||
{
|
||||
static char buf[1000];
|
||||
switch(status) {
|
||||
case XMLTV_GRABBER_UNCONFIGURED:
|
||||
snprintf(buf, sizeof(buf),
|
||||
"This grabber is not configured, you need to configure it "
|
||||
"manually by running '%s --configure' in a shell",
|
||||
xg->xg_path);
|
||||
return buf;
|
||||
|
||||
case XMLTV_GRABBER_DYSFUNCTIONAL:
|
||||
snprintf(buf, sizeof(buf),
|
||||
"This grabber does not produce valid XML, please check "
|
||||
"manually by running '%s' in a shell",
|
||||
xg->xg_path);
|
||||
return buf;
|
||||
|
||||
case XMLTV_GRABBER_ENQUEUED:
|
||||
case XMLTV_GRABBER_GRABBING:
|
||||
return "Grabbing, please wait";
|
||||
|
||||
|
||||
default:
|
||||
return "Unknown status";
|
||||
}
|
||||
}
|
||||
|
|
61
epg_xmltv.h
61
epg_xmltv.h
|
@ -19,6 +19,67 @@
|
|||
#ifndef XMLTV_H
|
||||
#define XMLTV_H
|
||||
|
||||
|
||||
LIST_HEAD(xmltv_grabber_list, xmltv_grabber);
|
||||
TAILQ_HEAD(xmltv_grabber_queue, xmltv_grabber);
|
||||
|
||||
LIST_HEAD(xmltv_channel_list, xmltv_channel);
|
||||
|
||||
typedef struct xmltv_grabber {
|
||||
LIST_ENTRY(xmltv_grabber) xg_link;
|
||||
char *xg_path;
|
||||
char *xg_title;
|
||||
char *xg_identifier;
|
||||
|
||||
enum {
|
||||
XMLTV_GRABBER_DISABLED,
|
||||
XMLTV_GRABBER_UNCONFIGURED,
|
||||
XMLTV_GRABBER_DYSFUNCTIONAL,
|
||||
XMLTV_GRABBER_ENQUEUED,
|
||||
XMLTV_GRABBER_GRABBING,
|
||||
XMLTV_GRABBER_IDLE,
|
||||
} xg_status;
|
||||
|
||||
TAILQ_ENTRY(xmltv_grabber) xg_work_link;
|
||||
|
||||
time_t xg_nextgrab;
|
||||
|
||||
struct xmltv_channel_list xg_channels;
|
||||
|
||||
} xmltv_grabber_t;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A channel in the XML-TV world
|
||||
*/
|
||||
typedef struct xmltv_channel {
|
||||
LIST_ENTRY(xmltv_channel) xc_link;
|
||||
char *xc_name;
|
||||
char *xc_displayname;
|
||||
char *xc_channel;
|
||||
char *xc_icon_url;
|
||||
|
||||
struct event_queue xc_events;
|
||||
|
||||
} xmltv_channel_t;
|
||||
|
||||
|
||||
#define XMLTVSTATUS_STARTING 0
|
||||
#define XMLTVSTATUS_FIND_GRABBERS_NOT_FOUND 1
|
||||
#define XMLTVSTATUS_RUNNING 2
|
||||
|
||||
extern int xmltv_status;
|
||||
extern struct xmltv_grabber_list xmltv_grabbers;
|
||||
|
||||
void xmltv_init(void);
|
||||
|
||||
const char *xmltv_grabber_status(xmltv_grabber_t *xg);
|
||||
|
||||
void xmltv_grabber_enable(xmltv_grabber_t *xg);
|
||||
|
||||
xmltv_grabber_t *xmltv_grabber_find(const char *id);
|
||||
|
||||
const char *xmltv_grabber_status_long(xmltv_grabber_t *xg, int status);
|
||||
|
||||
#endif /* __XMLTV_H__ */
|
||||
|
|
7
notify.c
7
notify.c
|
@ -23,6 +23,7 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "tvhead.h"
|
||||
#include "notify.h"
|
||||
#include "ajaxui/ajaxui_mailbox.h"
|
||||
|
||||
void
|
||||
|
@ -59,3 +60,9 @@ notify_tda_change(th_dvb_adapter_t *tda)
|
|||
{
|
||||
ajax_mailbox_tda_change(tda);
|
||||
}
|
||||
|
||||
void
|
||||
notify_xmltv_grabber_status_change(struct xmltv_grabber *xg, int status)
|
||||
{
|
||||
ajax_mailbox_xmltv_grabber_status_change(xg, status);
|
||||
}
|
||||
|
|
17
notify.h
17
notify.h
|
@ -19,14 +19,21 @@
|
|||
#ifndef NOTIFY_H_
|
||||
#define NOTIFY_H_
|
||||
|
||||
void notify_tdmi_state_change(th_dvb_mux_instance_t *tdmi);
|
||||
struct xmltv_grabber;
|
||||
struct th_dvb_mux_instance;
|
||||
struct th_dvb_adapterr;
|
||||
|
||||
void notify_tdmi_name_change(th_dvb_mux_instance_t *tdmi);
|
||||
void notify_tdmi_state_change(struct th_dvb_mux_instance *tdmi);
|
||||
|
||||
void notify_tdmi_status_change(th_dvb_mux_instance_t *tdmi);
|
||||
void notify_tdmi_name_change(struct th_dvb_mux_instance *tdmi);
|
||||
|
||||
void notify_tdmi_services_change(th_dvb_mux_instance_t *tdmi);
|
||||
void notify_tdmi_status_change(struct th_dvb_mux_instance *tdmi);
|
||||
|
||||
void notify_tda_change(th_dvb_adapter_t *tda);
|
||||
void notify_tdmi_services_change(struct th_dvb_mux_instance *tdmi);
|
||||
|
||||
void notify_tda_change(struct th_dvb_adapter *tda);
|
||||
|
||||
void notify_xmltv_grabber_status_change(struct xmltv_grabber *xg,
|
||||
int status);
|
||||
|
||||
#endif /* NOTIFY_H_ */
|
||||
|
|
21
spawn.c
21
spawn.c
|
@ -29,6 +29,8 @@
|
|||
#include "dispatch.h"
|
||||
#include "spawn.h"
|
||||
|
||||
extern char **environ;
|
||||
|
||||
static dtimer_t reaper_timer;
|
||||
|
||||
pthread_mutex_t spawn_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
@ -139,15 +141,22 @@ spawn_enq(const char *name, int pid)
|
|||
* *outp will point to the allocated buffer
|
||||
* The function will return the size of the buffer
|
||||
*/
|
||||
|
||||
int
|
||||
spawn_and_store_stdout(const char *prog, char **outp)
|
||||
spawn_and_store_stdout(const char *prog, char *const argv[], char **outp)
|
||||
{
|
||||
pid_t p;
|
||||
int fd[2], r, totalsize = 0;
|
||||
char *outbuf;
|
||||
|
||||
struct spawn_output_buf_queue bufs;
|
||||
spawn_output_buf_t *b = NULL;
|
||||
const char *local_argv[2];
|
||||
|
||||
if(argv == NULL) {
|
||||
local_argv[0] = prog;
|
||||
local_argv[1] = NULL;
|
||||
argv = (void *)local_argv;
|
||||
}
|
||||
|
||||
if(pipe(fd) == -1)
|
||||
return -1;
|
||||
|
@ -167,7 +176,7 @@ spawn_and_store_stdout(const char *prog, char **outp)
|
|||
dup2(fd[1], 1);
|
||||
close(fd[1]);
|
||||
syslog(LOG_INFO, "spawn: Executing \"%s\"", prog);
|
||||
execl(prog, prog, NULL);
|
||||
execve(prog, argv, environ);
|
||||
syslog(LOG_ERR, "spawn: pid %d cannot execute %s -- %s",
|
||||
getpid(), prog, strerror(errno));
|
||||
close(1);
|
||||
|
@ -226,10 +235,6 @@ spawnv(const char *prog, char *const argv[])
|
|||
{
|
||||
pid_t p;
|
||||
|
||||
char *envp[1];
|
||||
|
||||
envp[0] = NULL;
|
||||
|
||||
p = fork();
|
||||
|
||||
if(p == -1) {
|
||||
|
@ -242,7 +247,7 @@ spawnv(const char *prog, char *const argv[])
|
|||
close(0);
|
||||
close(2);
|
||||
syslog(LOG_INFO, "spawn: Executing \"%s\"", prog);
|
||||
execve(prog, argv, envp);
|
||||
execve(prog, argv, environ);
|
||||
syslog(LOG_ERR, "spawn: pid %d cannot execute %s -- %s",
|
||||
getpid(), prog, strerror(errno));
|
||||
close(1);
|
||||
|
|
2
spawn.h
2
spawn.h
|
@ -19,7 +19,7 @@
|
|||
#ifndef SPAWN_H
|
||||
#define SPAWN_H
|
||||
|
||||
int spawn_and_store_stdout(const char *prog, char **outp);
|
||||
int spawn_and_store_stdout(const char *prog, char *const argv[], char **outp);
|
||||
|
||||
void spawn_init(void);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue