Some tidying up and enabled full config save/store again.

This commit is contained in:
Adam Sutton 2012-06-07 13:55:32 +01:00
parent e652b27822
commit 7d6201b90e
6 changed files with 225 additions and 195 deletions

View file

@ -32,15 +32,76 @@ uint32_t epggrab_interval;
epggrab_module_t* epggrab_module;
epggrab_module_list_t epggrab_modules;
/* Prototypes */
static void _epggrab_module_parse
( epggrab_module_t *mod, htsmsg_t *data );
/* **************************************************************************
* Threads
* Helpers
* *************************************************************************/
// TODO: should I put this in a thread
/*
* Run the parse
*/
static void _epggrab_module_parse
( epggrab_module_t *mod, htsmsg_t *data )
{
time_t tm1, tm2;
int save = 0;
epggrab_stats_t stats;
/* Parse */
memset(&stats, 0, sizeof(stats));
pthread_mutex_lock(&global_lock);
time(&tm1);
save |= mod->parse(mod, data, &stats);
time(&tm2);
if (save) epg_updated();
pthread_mutex_unlock(&global_lock);
htsmsg_destroy(data);
/* Debug stats */
tvhlog(LOG_DEBUG, mod->id, "parse took %d seconds", tm2 - tm1);
tvhlog(LOG_DEBUG, mod->id, " channels tot=%5d new=%5d mod=%5d",
stats.channels.total, stats.channels.created,
stats.channels.modified);
tvhlog(LOG_DEBUG, mod->id, " brands tot=%5d new=%5d mod=%5d",
stats.brands.total, stats.brands.created,
stats.brands.modified);
tvhlog(LOG_DEBUG, mod->id, " seasons tot=%5d new=%5d mod=%5d",
stats.seasons.total, stats.seasons.created,
stats.seasons.modified);
tvhlog(LOG_DEBUG, mod->id, " episodes tot=%5d new=%5d mod=%5d",
stats.episodes.total, stats.episodes.created,
stats.episodes.modified);
tvhlog(LOG_DEBUG, mod->id, " broadcasts tot=%5d new=%5d mod=%5d",
stats.broadcasts.total, stats.broadcasts.created,
stats.broadcasts.modified);
}
/*
* Grab from module
*/
static void _epggrab_module_grab ( epggrab_module_t *mod )
{
time_t tm1, tm2;
htsmsg_t *data;
/* Grab */
time(&tm1);
data = mod->trans(mod, mod->grab(mod));
time(&tm2);
/* Process */
if ( data ) {
tvhlog(LOG_DEBUG, mod->id, "grab took %d seconds", tm2 - tm1);
_epggrab_module_parse(mod, data);
} else {
tvhlog(LOG_WARNING, mod->id, "grab returned no data");
}
}
/*
* Socket handler
*
* TODO: could make this threaded to allow multiple simultaneous inputs
*/
static void _epggrab_socket_handler ( epggrab_module_t *mod, int s )
{
size_t outlen;
@ -65,7 +126,50 @@ static void _epggrab_socket_handler ( epggrab_module_t *mod, int s )
}
}
// TODO: what happens if we terminate early?
/* **************************************************************************
* Threads
* *************************************************************************/
/*
* Thread (for internal grabbing)
*/
static void* _epggrab_internal_thread ( void* p )
{
int confver = -1; // force first run
struct timespec ts;
epggrab_module_t *mod;
/* Setup timeout */
ts.tv_nsec = 0;
ts.tv_sec = 0;
while ( 1 ) {
/* Check for config change */
pthread_mutex_lock(&epggrab_mutex);
while ( confver == epggrab_confver ) {
int err = pthread_cond_timedwait(&epggrab_cond, &epggrab_mutex, &ts);
if ( err == ETIMEDOUT ) break;
}
confver = epggrab_confver;
mod = NULL;//epggrab_module;
ts.tv_sec += epggrab_interval;
pthread_mutex_unlock(&epggrab_mutex);
/* Run grabber */
if (mod) _epggrab_module_grab(mod);
}
return NULL;
}
/*
* External (socket) grab thread
*
* TODO: I could common all of this up and have a single thread
* servicing all the available sockets, but we're unlikely to
* have a massive number of modules enabled anyway!
*/
static void *_epggrab_socket_thread ( void *p )
{
int s;
@ -82,7 +186,7 @@ static void *_epggrab_socket_thread ( void *p )
}
/* **************************************************************************
* Modules
* Base Module functions
* *************************************************************************/
static int _ch_id_cmp ( void *a, void *b )
@ -93,6 +197,7 @@ static int _ch_id_cmp ( void *a, void *b )
static int _ch_match ( epggrab_channel_t *ec, channel_t *ch )
{
// TODO: this needs implementing
return 0;
}
@ -169,6 +274,7 @@ int epggrab_module_channel_mod ( epggrab_module_t *mod, channel_t *ch )
epggrab_channel_t *epggrab_module_channel_create ( epggrab_module_t *mod )
{
// TODO: implement this
return NULL;
}
@ -180,41 +286,17 @@ epggrab_channel_t *epggrab_module_channel_find
return RB_FIND(mod->channels, &skel, link, _ch_id_cmp);
}
epggrab_module_t* epggrab_module_find_by_id ( const char *id )
{
epggrab_module_t *m;
LIST_FOREACH(m, &epggrab_modules, link) {
if ( !strcmp(m->id, id) ) return m;
}
return NULL;
}
htsmsg_t *epggrab_module_list ( void )
{
epggrab_module_t *m;
htsmsg_t *e, *a = htsmsg_create_list();
LIST_FOREACH(m, &epggrab_modules, link) {
e = htsmsg_create_map();
htsmsg_add_str(e, "id", m->id);
if(m->name) htsmsg_add_str(e, "name", m->name);
if(m->path) htsmsg_add_str(e, "path", m->path);
htsmsg_add_u32(e, "flags", m->flags);
htsmsg_add_u32(e, "enabled", m->enabled);
htsmsg_add_msg(a, NULL, e);
}
return a;
}
// Uses a unix domain socket, but we could extend to a remote interface
// to allow EPG data to be remotely generated (another machine in the local
// network etc...)
void epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e )
// TODO: could use TCP socket to allow remote access
int epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e )
{
pthread_t tid;
pthread_attr_t tattr;
struct sockaddr_un addr;
assert(mod->path);
assert(mod->flags & EPGGRAB_MODULE_EXTERNAL);
/* Ignore */
if ( mod->enabled == e ) return 0;
/* Disable */
if (!e) {
@ -222,7 +304,6 @@ void epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e )
close(mod->sock);
unlink(mod->path);
mod->sock = 0;
// TODO: I don't shutdown the thread!
/* Enable */
} else {
@ -234,18 +315,28 @@ void epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e )
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, mod->path, 100);
// TODO: possibly asserts here not a good idea if I make the socket
// path configurable
assert(bind(mod->sock, (struct sockaddr*)&addr,
sizeof(struct sockaddr_un)) == 0);
if ( bind(mod->sock, (struct sockaddr*)&addr,
sizeof(struct sockaddr_un)) != 0 ) {
tvhlog(LOG_ERR, mod->id, "failed to bind socket");
close(mod->sock);
mod->sock = 0;
return 0;
}
assert(listen(mod->sock, 5) == 0);
if ( listen(mod->sock, 5) != 0 ) {
tvhlog(LOG_ERR, mod->id, "failed to listen on socket");
close(mod->sock);
mod->sock = 0;
return 0;
}
tvhlog(LOG_DEBUG, mod->id, "starting socket thread");
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &tattr, _epggrab_socket_thread, mod);
tvhlog(LOG_DEBUG, mod->id, "enabled socket");
}
mod->enabled = e;
return 1;
}
char *epggrab_module_grab ( epggrab_module_t *mod )
@ -295,7 +386,8 @@ const char *epggrab_module_socket_path ( epggrab_module_t *mod )
static void _epggrab_load ( void )
{
htsmsg_t *m;
epggrab_module_t *mod;
htsmsg_t *m, *a;
const char *str;
/* No config */
@ -307,13 +399,20 @@ static void _epggrab_load ( void )
htsmsg_get_u32(m, "interval", &epggrab_interval);
if ( (str = htsmsg_get_str(m, "module")) )
epggrab_module = epggrab_module_find_by_id(str);
// TODO: module states
if ( (a = htsmsg_get_map(m, "mod_enabled")) ) {
LIST_FOREACH(mod, &epggrab_modules, link) {
if (htsmsg_get_u32_or_default(a, mod->id, 0)) {
if (mod->enable) mod->enable(mod, 1);
}
}
}
htsmsg_destroy(m);
}
void epggrab_save ( void )
{
htsmsg_t *m;
epggrab_module_t *mod;
htsmsg_t *m, *a;
/* Register */
epggrab_confver++;
@ -325,6 +424,11 @@ void epggrab_save ( void )
htsmsg_add_u32(m, "interval", epggrab_interval);
if ( epggrab_module )
htsmsg_add_str(m, "module", epggrab_module->id);
a = htsmsg_create_map();
LIST_FOREACH(mod, &epggrab_modules, link) {
if (mod->enabled) htsmsg_add_u32(a, mod->id, 1);
}
htsmsg_add_msg(m, "mod_enabled", a);
hts_settings_save(m, "epggrab/config");
htsmsg_destroy(m);
}
@ -353,10 +457,12 @@ int epggrab_set_interval ( uint32_t interval )
int epggrab_set_module ( epggrab_module_t *mod )
{
int save = 0;
if ( mod && epggrab_module != mod ) {
assert(mod->grab);
assert(mod->trans);
assert(mod->parse);
if ( epggrab_module != mod ) {
if (mod) {
assert(mod->grab);
assert(mod->trans);
assert(mod->parse);
}
epggrab_module = mod;
save = 1;
}
@ -371,11 +477,10 @@ int epggrab_set_module_by_id ( const char *id )
int epggrab_enable_module ( epggrab_module_t *mod, uint8_t e )
{
int save = 0;
if ( e != mod->enabled ) {
assert(mod->trans);
assert(mod->parse);
if (mod->enable) {
save = mod->enable(mod, e);
} else if ( e != mod->enabled ) {
mod->enabled = e;
if (mod->enable) mod->enable(mod, e);
save = 1;
}
return save;
@ -386,112 +491,6 @@ int epggrab_enable_module_by_id ( const char *id, uint8_t e )
return epggrab_enable_module(epggrab_module_find_by_id(id), e);
}
/* **************************************************************************
* Module Execution
* *************************************************************************/
/*
* Run the parse
*/
static void _epggrab_module_parse
( epggrab_module_t *mod, htsmsg_t *data )
{
time_t tm1, tm2;
int save = 0;
epggrab_stats_t stats;
/* Parse */
memset(&stats, 0, sizeof(stats));
pthread_mutex_lock(&global_lock);
time(&tm1);
save |= mod->parse(mod, data, &stats);
time(&tm2);
if (save) epg_updated();
pthread_mutex_unlock(&global_lock);
htsmsg_destroy(data);
/* Debug stats */
tvhlog(LOG_DEBUG, mod->id, "parse took %d seconds", tm2 - tm1);
tvhlog(LOG_DEBUG, mod->id, " channels tot=%5d new=%5d mod=%5d",
stats.channels.total, stats.channels.created,
stats.channels.modified);
tvhlog(LOG_DEBUG, mod->id, " brands tot=%5d new=%5d mod=%5d",
stats.brands.total, stats.brands.created,
stats.brands.modified);
tvhlog(LOG_DEBUG, mod->id, " seasons tot=%5d new=%5d mod=%5d",
stats.seasons.total, stats.seasons.created,
stats.seasons.modified);
tvhlog(LOG_DEBUG, mod->id, " episodes tot=%5d new=%5d mod=%5d",
stats.episodes.total, stats.episodes.created,
stats.episodes.modified);
tvhlog(LOG_DEBUG, mod->id, " broadcasts tot=%5d new=%5d mod=%5d",
stats.broadcasts.total, stats.broadcasts.created,
stats.broadcasts.modified);
}
/*
* Grab from module
*/
static void _epggrab_module_grab ( epggrab_module_t *mod )
{
time_t tm1, tm2;
htsmsg_t *data;
/* Grab */
time(&tm1);
printf("mod = %s\n", mod->id);
printf("trans = %p, grab = %p\n", mod->trans, mod->grab);
data = mod->trans(mod, mod->grab(mod));
time(&tm2);
/* Process */
if ( data ) {
tvhlog(LOG_DEBUG, mod->id, "grab took %d seconds", tm2 - tm1);
_epggrab_module_parse(mod, data);
} else {
tvhlog(LOG_WARNING, mod->id, "grab returned no data");
}
}
/*
* Thread (for internal grabbing)
*/
static void* _epggrab_thread ( void* p )
{
int confver = 0;
struct timespec ts;
pthread_mutex_lock(&epggrab_mutex);
epggrab_module_t *mod;
/* Load */
_epggrab_load();
/* Setup timeout */
ts.tv_nsec = 0;
ts.tv_sec = 0;
while ( 1 ) {
/* Check for config change */
while ( confver == epggrab_confver ) {
int err = pthread_cond_timedwait(&epggrab_cond, &epggrab_mutex, &ts);
if ( err == ETIMEDOUT ) break;
}
confver = epggrab_confver;
mod = NULL;//epggrab_module;
ts.tv_sec += epggrab_interval;
/* Run grabber (without lock) */
if (mod) {
pthread_mutex_unlock(&epggrab_mutex);
_epggrab_module_grab(mod);
pthread_mutex_lock(&epggrab_mutex);
}
}
return NULL;
}
/* **************************************************************************
* Global Functions
* *************************************************************************/
@ -511,12 +510,15 @@ void epggrab_init ( void )
xmltv_init(&epggrab_modules);
pyepg_init(&epggrab_modules);
/* Start thread */
/* Load config */
_epggrab_load();
/* Start internal grab thread */
pthread_t tid;
pthread_attr_t tattr;
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &tattr, _epggrab_thread, NULL);
pthread_create(&tid, &tattr, _epggrab_internal_thread, NULL);
}
void epggrab_channel_add ( channel_t *ch )
@ -548,3 +550,28 @@ void epggrab_channel_mod ( channel_t *ch )
m->ch_save(m);
}
}
epggrab_module_t* epggrab_module_find_by_id ( const char *id )
{
epggrab_module_t *m;
LIST_FOREACH(m, &epggrab_modules, link) {
if ( !strcmp(m->id, id) ) return m;
}
return NULL;
}
htsmsg_t *epggrab_module_list ( void )
{
epggrab_module_t *m;
htsmsg_t *e, *a = htsmsg_create_list();
LIST_FOREACH(m, &epggrab_modules, link) {
e = htsmsg_create_map();
htsmsg_add_str(e, "id", m->id);
if(m->name) htsmsg_add_str(e, "name", m->name);
if(m->path) htsmsg_add_str(e, "path", m->path);
htsmsg_add_u32(e, "flags", m->flags);
htsmsg_add_u32(e, "enabled", m->enabled);
htsmsg_add_msg(a, NULL, e);
}
return a;
}

View file

@ -42,11 +42,16 @@ typedef struct epggrab_channel
} epggrab_channel_t;
/*
* Channel list
* Channel list structure
*/
RB_HEAD(epggrab_channel_tree, epggrab_channel);
typedef struct epggrab_channel_tree epggrab_channel_tree_t;
/*
* Access functions
*/
htsmsg_t* epggrab_channel_list ( void );
/* **************************************************************************
* Grabber Modules
* *************************************************************************/
@ -56,11 +61,9 @@ typedef struct epggrab_module epggrab_module_t;
/*
* Grabber flags
*/
#define EPGGRAB_MODULE_SYNC 0x01
#define EPGGRAB_MODULE_ASYNC 0x02
#define EPGGRAB_MODULE_SIMPLE 0x04
#define EPGGRAB_MODULE_ADVANCED 0x08
#define EPGGRAB_MODULE_EXTERNAL 0x10
#define EPGGRAB_MODULE_SIMPLE 0x01
#define EPGGRAB_MODULE_EXTERNAL 0x02
#define EPGGRAB_MODULE_SPECIAL 0x04
/*
* Grabber base class
@ -72,13 +75,13 @@ struct epggrab_module
const char *id; ///< Module identifier
const char *name; ///< Module name (for display)
const char *path; ///< Module path (for fixed config)
const uint8_t flags; ///< Mode flags
const uint8_t flags; ///< Mode flag
uint8_t enabled; ///< Whether the module is enabled
int sock; ///< Socket descriptor
epggrab_channel_tree_t *channels; ///< Channel list
/* Enable/Disable */
void (*enable) ( epggrab_module_t *m, uint8_t e );
int (*enable) ( epggrab_module_t *m, uint8_t e );
/* Grab/Translate/Parse */
char* (*grab) ( epggrab_module_t *m );
@ -96,20 +99,26 @@ struct epggrab_module
};
/*
* Default module functions (shared by pyepg and xmltv)
* Default module functions
*
* Kinda like a base class (shared by current modules xmltv and pyepg)
*/
const char *epggrab_module_socket_path ( epggrab_module_t *m );
void epggrab_module_enable_socket ( epggrab_module_t *m, uint8_t e );
char *epggrab_module_grab ( epggrab_module_t *m );
int epggrab_module_enable_socket ( epggrab_module_t *m, uint8_t e );
const char *epggrab_module_socket_path ( epggrab_module_t *m );
char *epggrab_module_grab ( epggrab_module_t *m );
htsmsg_t *epggrab_module_trans_xml ( epggrab_module_t *m, char *d );
void epggrab_module_channels_load ( epggrab_module_t *m );
void epggrab_module_channels_save ( epggrab_module_t *m, const char *path );
int epggrab_module_channel_add ( epggrab_module_t *m, channel_t *ch );
int epggrab_module_channel_rem ( epggrab_module_t *m, channel_t *ch );
int epggrab_module_channel_mod ( epggrab_module_t *m, channel_t *ch );
epggrab_channel_t *epggrab_module_channel_create
( epggrab_module_t *m );
epggrab_channel_t *epggrab_module_channel_find
epggrab_channel_t *epggrab_module_channel_find
( epggrab_module_t *m, const char *id );
/*
@ -124,11 +133,6 @@ typedef struct epggrab_module_list epggrab_module_list_t;
epggrab_module_t* epggrab_module_find_by_id ( const char *id );
htsmsg_t* epggrab_module_list ( void );
/*
* Channel list
*/
htsmsg_t* epggrab_channel_list ( void );
/* **************************************************************************
* Configuration
* *************************************************************************/

View file

@ -92,7 +92,7 @@ void eit_init ( epggrab_module_list_t *list )
{
_eit_mod.id = strdup("eit");
_eit_mod.name = strdup("EIT: On-Air Grabber");
*((uint8_t*)&_eit_mod.flags) = EPGGRAB_MODULE_ASYNC;
*((uint8_t*)&_eit_mod.flags) = EPGGRAB_MODULE_SPECIAL;
LIST_INSERT_HEAD(list, &_eit_mod, link);
// Note: this is mostly ignored anyway as EIT is treated as a special case!
}

View file

@ -421,6 +421,8 @@ static void _xmltv_load_grabbers ( epggrab_module_list_t *list )
mod->name = malloc(200);
sprintf((char*)mod->name, "XMLTV: %s", &outbuf[n]);
*((uint8_t*)&mod->flags) = EPGGRAB_MODULE_SIMPLE;
mod->grab = epggrab_module_grab;
mod->trans = epggrab_module_trans_xml;
mod->parse = _xmltv_parse;
LIST_INSERT_HEAD(list, mod, link);
p = n = i + 1;
@ -441,15 +443,16 @@ void xmltv_init ( epggrab_module_list_t *list )
mod = calloc(1, sizeof(epggrab_module_t));
mod->id = strdup("xmltv");
mod->name = strdup("XMLTV");
*((uint8_t*)&mod->flags) = EPGGRAB_MODULE_EXTERNAL;
mod->path = epggrab_module_socket_path(mod);
mod->enable = epggrab_module_enable_socket;
mod->grab = epggrab_module_grab;
mod->trans = epggrab_module_trans_xml;
mod->parse = _xmltv_parse;
mod->channels = &_xmltv_channels;
mod->ch_add = epggrab_module_channel_add;
mod->ch_rem = epggrab_module_channel_rem;
mod->ch_mod = epggrab_module_channel_mod;
mod->ch_save = _xmltv_save;
*((uint8_t*)&mod->flags) = EPGGRAB_MODULE_EXTERNAL;
LIST_INSERT_HEAD(list, mod, link);
/* Standard modules */

View file

@ -518,17 +518,12 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque)
if ( (str = http_arg_get(&hc->hc_req_args, "module")) )
save |= epggrab_set_module_by_id(str);
if ( (str = http_arg_get(&hc->hc_req_args, "external")) ) {
printf("got external\n");
if ( (array = htsmsg_json_deserialize(str)) ) {
printf("got array\n");
HTSMSG_FOREACH(f, array) {
if ( (e = htsmsg_get_map_by_field(f)) ) {
printf("got field\n");
str = htsmsg_get_str(e, "id");
printf("id = %s\n", str);
u32 = 0;
htsmsg_get_u32(e, "enabled", &u32);
printf("enabled = %d\n", u32);
if ( str ) save |= epggrab_enable_module_by_id(str, u32);
}
}
@ -543,7 +538,6 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque)
return HTTP_STATUS_BAD_REQUEST;
}
htsmsg_print(out);
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output_content(hc, "text/x-json; charset=UTF-8");

View file

@ -7,8 +7,8 @@ tvheadend.epggrab = function() {
/*
* Module lists (I'm sure there is a better way!)
*/
var EPGGRAB_MODULE_SIMPLE = 0x04;
var EPGGRAB_MODULE_EXTERNAL = 0x10;
var EPGGRAB_MODULE_SIMPLE = 0x01;
var EPGGRAB_MODULE_EXTERNAL = 0x02;
var moduleStore = new Ext.data.JsonStore({
root : 'entries',
@ -27,6 +27,8 @@ tvheadend.epggrab = function() {
moduleStore.filterBy(function(r) {
return r.get('flags') & EPGGRAB_MODULE_SIMPLE;
});
r = new simpleModuleStore.recordType({ id: '', name : 'Disabled'});
simpleModuleStore.add(r);
moduleStore.each(function(r) {
simpleModuleStore.add(r.copy());
});
@ -150,14 +152,14 @@ tvheadend.epggrab = function() {
{
header : 'Path',
dataIndex : 'path',
width : 200,
width : 300,
sortable : false,
// TODO: editable?
},
{
dataIndex : 'enabled',
header : 'Enabled',
width : 50,
width : 100,
sortable : false,
editor : new Ext.form.Checkbox(),
// TODO: newer versions of extjs provide proper checkbox in grid
@ -175,7 +177,7 @@ tvheadend.epggrab = function() {
var externalGrid = new Ext.grid.EditorGridPanel({
store : externalModuleStore,
cm : externalColumnModel,
width : 450,
width : 600,
height : 150,
frame : false,
viewConfig : {