From 73286da38af95fb17f9e1a67d270c8a8fa56d8f0 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 1 Jun 2012 22:17:47 +0100 Subject: [PATCH] Complete re-write of the epggrab infastructure, trying to generalise stuff a bit more to make config pages easier and to cover the common cases. --- src/cron.c | 12 +- src/cron.h | 2 +- src/epggrab.c | 737 +++++++++++++++++++++++--------------------- src/epggrab.h | 153 +++++---- src/epggrab/eit.c | 23 +- src/epggrab/eit.h | 2 +- src/epggrab/pyepg.c | 99 +++--- src/epggrab/pyepg.h | 2 +- src/epggrab/xmltv.c | 47 ++- src/epggrab/xmltv.h | 3 +- 10 files changed, 581 insertions(+), 499 deletions(-) diff --git a/src/cron.c b/src/cron.c index efdd8c32..6d7a9887 100644 --- a/src/cron.c +++ b/src/cron.c @@ -91,9 +91,11 @@ static int _cron_parse ( cron_t *cron, const char *str ) return 1; // TODO: do proper validation } -cron_t *cron_create ( void ) +cron_t *cron_create ( const char *str ) { - return calloc(1, sizeof(cron_t)); + cron_t *c = calloc(1, sizeof(cron_t)); + if (str) cron_set_string(c, str); + return c; } void cron_destroy ( cron_t *cron ) @@ -160,11 +162,7 @@ void cron_serialize ( cron_t *cron, htsmsg_t *msg ) cron_t *cron_deserialize ( htsmsg_t *msg ) { - // TODO: validate the string! - cron_t *cron = cron_create(); - const char *str = htsmsg_get_str(msg, "cron"); - if (str) cron_set_string(cron, str); - return cron; + return cron_create(htsmsg_get_str(msg, "cron")); } #if 0 diff --git a/src/cron.h b/src/cron.h index f4d749cc..6c42b11d 100644 --- a/src/cron.h +++ b/src/cron.h @@ -13,7 +13,7 @@ typedef struct cron cron_t; -cron_t *cron_create ( void ); +cron_t *cron_create ( const char *str ); void cron_destroy ( cron_t *cron ); const char *cron_get_string ( cron_t *cron ); int cron_set_string ( cron_t *cron, const char *str ); diff --git a/src/epggrab.c b/src/epggrab.c index b1107b0c..c8a5da95 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -14,160 +14,433 @@ #include "channels.h" /* Thread protection */ -int epggrab_confver; -pthread_mutex_t epggrab_mutex; -pthread_cond_t epggrab_cond; +int epggrab_confver; +pthread_mutex_t epggrab_mutex; +pthread_cond_t epggrab_cond; /* Config */ -uint32_t epggrab_advanced; -uint32_t epggrab_eit; -uint32_t epggrab_interval; -epggrab_module_t* epggrab_module; -epggrab_sched_list_t epggrab_schedule; +uint32_t epggrab_advanced; +uint32_t epggrab_eit; +uint32_t epggrab_interval; +epggrab_module_t* epggrab_module; +epggrab_sched_list_t epggrab_schedule; +epggrab_module_list_t epggrab_modules; -/* Modules */ -LIST_HEAD(epggrab_module_list, epggrab_module); -struct epggrab_module_list epggrab_modules; +/* ************************************************************************** + * Modules + * *************************************************************************/ -/* Internal prototypes */ -static void* _epggrab_thread ( void* ); -static time_t _epggrab_thread_simple ( void ); -static time_t _epggrab_thread_advanced ( void ); -static void _epggrab_load ( void ); -static void _epggrab_save ( void ); -static void _epggrab_set_schedule ( int, epggrab_sched_t* ); +static int _ch_id_cmp ( void *a, void *b ) +{ + return strcmp(((epggrab_channel_t*)a)->id, + ((epggrab_channel_t*)b)->id); +} -/* - * Initialise - */ -void epggrab_init ( void ) +static int _ch_match ( epggrab_channel_t *ec, channel_t *ch ) +{ + return 0; +} + +void epggrab_module_channels_load ( epggrab_module_t *mod ) +{ + char path[100]; + uint32_t chid; + htsmsg_t *m; + htsmsg_field_t *f; + epggrab_channel_t *ec; + channel_t *ch; + + sprintf(path, "epggrab/%s/channels", mod->id); + if ((m = hts_settings_load(path))) { + HTSMSG_FOREACH(f, m) { + if ( !htsmsg_get_u32(m, f->hmf_name, &chid) ) { + ch = channel_find_by_identifier(chid); + if (ch) { + ec = calloc(1, sizeof(epggrab_channel_t)); + ec->id = strdup(f->hmf_name); + ec->channel = ch; + RB_INSERT_SORTED(mod->channels, ec, link, _ch_id_cmp); + } + } + } + } +} + +void epggrab_module_channels_save ( epggrab_module_t *mod ) +{ + char path[100]; + epggrab_channel_t *c; + htsmsg_t *m = htsmsg_create_map(); + + sprintf(path, "epggrab/%s/channels", mod->id); + RB_FOREACH(c, mod->channels, link) { + if (c->channel) htsmsg_add_u32(m, c->id, c->channel->ch_id); + } + hts_settings_save(m, path); +} + +int epggrab_module_channel_add ( epggrab_module_t *mod, channel_t *ch ) +{ + int save = 0; + epggrab_channel_t *egc; + RB_FOREACH(egc, mod->channels, link) { + if (_ch_match(egc, ch) ) { + save = 1; + egc->channel = ch; + break; + } + } + return save; +} + +int epggrab_module_channel_rem ( epggrab_module_t *mod, channel_t *ch ) +{ + int save = 0; + epggrab_channel_t *egc; + RB_FOREACH(egc, mod->channels, link) { + if (egc->channel == ch) { + save = 1; + egc->channel = NULL; + break; + } + } + return save; +} + +int epggrab_module_channel_mod ( epggrab_module_t *mod, channel_t *ch ) +{ + return epggrab_module_channel_add(mod, ch); +} + + +epggrab_channel_t *epggrab_module_channel_create ( epggrab_module_t *mod ) +{ + return NULL; +} + +epggrab_channel_t *epggrab_module_channel_find + ( epggrab_module_t *mod, const char *id ) +{ + epggrab_channel_t skel; + skel.id = (char*)id; + 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; - - /* Defaults */ - epggrab_advanced = 0; - epggrab_eit = 1; // on air grab enabled - epggrab_interval = 12 * 3600; // hours - epggrab_module = NULL; // disabled - - /* Initialise modules */ - m = eit_init(); - LIST_INSERT_HEAD(&epggrab_modules, m, glink); -#if TODO_XMLTV_SUPPORT - m = xmltv_init(); - LIST_INSERT_HEAD(&epggrab_modules, m, glink); -#endif - m = pyepg_init(); - LIST_INSERT_HEAD(&epggrab_modules, m, glink); - - /* Start 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); + LIST_FOREACH(m, &epggrab_modules, link) { + if ( !strcmp(m->id, id) ) return m; + } + return NULL; } +htsmsg_t *epggrab_module_list ( void ) +{ + htsmsg_t *a = htsmsg_create_list(); + return a; +} + +/* ************************************************************************** + * Configuration + * *************************************************************************/ + +static int _update_str ( char **dst, const char *src ) +{ + int save = 0; + if ( !(*dst) && src ) { + *dst = strdup(src); + save = 1; + } else if ( (*dst) && !src ) { + free(*dst); + *dst = NULL; + save = 1; + } else if ( strcmp(*dst, src) ) { + free(*dst); + *dst = strdup(src); + save = 1; + } + return save; +} + +static int _epggrab_schedule_deserialize ( htsmsg_t *m ) +{ + int save = 0; + htsmsg_t *a, *e; + htsmsg_field_t *f; + epggrab_sched_t *es; + epggrab_module_t *mod; + const char *str; + + es = LIST_FIRST(&epggrab_schedule); + if ( (a = htsmsg_get_list(m, "schedule")) ) { + HTSMSG_FOREACH(f, a) { + if ((e = htsmsg_get_map_by_field(f))) { + if (es) { + es = LIST_NEXT(es, link); + } else { + es = calloc(1, sizeof(epggrab_sched_t)); + save = 1; + LIST_INSERT_HEAD(&epggrab_schedule, es, link); + } + + mod = NULL; + if ((str = htsmsg_get_str(e, "module"))) + mod = epggrab_module_find_by_id(str); + if (es->mod != mod) { + es->mod = mod; + save = 1; + } + + save |= _update_str(&es->cmd, htsmsg_get_str(e, "command")); + save |= _update_str(&es->opts, htsmsg_get_str(e, "options")); + str = htsmsg_get_str(e, "cron"); + if (es->cron) { + if (!str) { + free(es->cron); + es->cron = NULL; + save = 1; + } else { + save |= cron_set_string(es->cron, str); + } + } else if (str) { + es->cron = cron_create(str); + save = 1; + } + } + } + } + + if (es) + while ( LIST_NEXT(es, link) ) { + LIST_REMOVE(es, link); + save = 1; + } + + return save; +} + +static htsmsg_t *_epggrab_schedule_serialize ( void ) +{ + epggrab_sched_t *es; + htsmsg_t *e, *a; + a = htsmsg_create_list(); + LIST_FOREACH(es, &epggrab_schedule, link) { + e = htsmsg_create_map(); + if ( es->mod ) htsmsg_add_str(e, "module", es->mod->id); + if ( es->cmd ) htsmsg_add_str(e, "command", es->cmd); + if ( es->opts ) htsmsg_add_str(e, "options", es->opts); + if ( es->cron ) cron_serialize(es->cron, e); + htsmsg_add_msg(a, NULL, e); + } + return a; +} + +static void _epggrab_load ( void ) +{ + htsmsg_t *m, *a; + const char *str; + + /* No config */ + if ((m = hts_settings_load("epggrab/config")) == NULL) + return; + + /* Load settings */ + htsmsg_get_u32(m, "advanced", &epggrab_advanced); + htsmsg_get_u32(m, "eit", &epggrab_eit); + htsmsg_get_u32(m, "interval", &epggrab_interval); + if ( (str = htsmsg_get_str(m, "module")) ) + epggrab_module = epggrab_module_find_by_id(str); + if ( (a = htsmsg_get_list(m, "schedule")) ) + _epggrab_schedule_deserialize(a); + htsmsg_destroy(m); +} + +void epggrab_save ( void ) +{ + htsmsg_t *m; + + /* Register */ + epggrab_confver++; + pthread_cond_signal(&epggrab_cond); + + /* Save */ + m = htsmsg_create_map(); + htsmsg_add_u32(m, "advanced", epggrab_advanced); + htsmsg_add_u32(m, "eitenabled", epggrab_eitenabled); + htsmsg_add_u32(m, "interval", epggrab_interval); + if ( epggrab_module ) + htsmsg_add_str(m, "module", epggrab_module->id); + htsmsg_add_msg(m, "schedule", _epggrab_schedule_serialize()); + hts_settings_save(m, "epggrab/config"); + htsmsg_destroy(m); +} + +int epggrab_set_advanced ( uint32_t advanced ) +{ + int save = 0; + if ( epggrab_advanced != advanced ) { + save = 1; + epggrab_advanced = advanced; + } + return save; +} + +int epggrab_set_eitenabled ( uint32_t eitenabled ) +{ + int save = 0; + if ( epggrab_eit != eitenabled ) { + save = 1; + eitenabled = eitenabled; + } + return save; +} + +int epggrab_set_interval ( uint32_t interval ) +{ + int save = 0; + if ( epggrab_interval != interval ) { + save = 1; + epggrab_interval = interval; + } + return save; +} + +int epggrab_set_module ( epggrab_module_t *mod ) +{ + int save = 0; + if ( mod && epggrab_module != mod ) { + save = 1; + epggrab_module = mod; + } + return save; +} + +int epggrab_set_module_by_id ( const char *id ) +{ + return epggrab_set_module(epggrab_module_find_by_id(id)); +} + +int epggrab_set_schedule ( htsmsg_t *sched ) +{ + return _epggrab_schedule_deserialize(sched); +} + +htsmsg_t *epggrab_get_schedule ( void ) +{ + return _epggrab_schedule_serialize(); +} + +/* ************************************************************************** + * Module Execution + * *************************************************************************/ + /* * Grab from module */ -static void _epggrab_module_run ( epggrab_module_t *mod, const char *opts ) +static void _epggrab_module_run + ( epggrab_module_t *mod, const char *icmd, const char *iopts ) { int save = 0; time_t tm1, tm2; htsmsg_t *data; epggrab_stats_t stats; - memset(&stats, 0, sizeof(stats)); + char *cmd = NULL, *opts = NULL; /* Check */ - if ( !mod ) return; + if ( !mod || !mod->grab || !mod->parse ) return; + + /* Dup before unlock */ + if ( !icmd ) cmd = strdup(icmd); + if ( !iopts ) opts = strdup(iopts); + pthread_mutex_unlock(&epggrab_mutex); /* Grab */ time(&tm1); - data = mod->grab(opts); + data = mod->grab(mod, cmd, opts); time(&tm2); /* Process */ if ( data ) { - tvhlog(LOG_DEBUG, mod->name(), "grab took %d seconds", tm2 - tm1); + + /* Parse */ + memset(&stats, 0, sizeof(stats)); + tvhlog(LOG_DEBUG, mod->id, "grab took %d seconds", tm2 - tm1); pthread_mutex_lock(&global_lock); time(&tm1); - save |= mod->parse(data, &stats); + 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->name(), "parse took %d seconds", tm2 - tm1); - tvhlog(LOG_DEBUG, mod->name(), " channels tot=%5d new=%5d mod=%5d", + 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->name(), " brands tot=%5d new=%5d mod=%5d", + 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->name(), " seasons tot=%5d new=%5d mod=%5d", + 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->name(), " episodes tot=%5d new=%5d mod=%5d", + 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->name(), " broadcasts tot=%5d new=%5d mod=%5d", + tvhlog(LOG_DEBUG, mod->id, " broadcasts tot=%5d new=%5d mod=%5d", stats.broadcasts.total, stats.broadcasts.created, stats.broadcasts.modified); } else { - tvhlog(LOG_WARNING, mod->name(), "grab returned no data"); + tvhlog(LOG_WARNING, mod->id, "grab returned no data"); } + + /* Free a re-lock */ + if (cmd) free(cmd); + if (opts) free(cmd); + pthread_mutex_lock(&epggrab_mutex); } /* * Simple run */ -time_t _epggrab_thread_simple ( void ) +static time_t _epggrab_thread_simple ( void ) { /* Copy config */ time_t ret = time(NULL) + epggrab_interval; epggrab_module_t* mod = epggrab_module; - /* Unlock */ - pthread_mutex_unlock(&epggrab_mutex); - /* Run the module */ - _epggrab_module_run(mod, NULL); - - /* Re-lock */ - pthread_mutex_lock(&epggrab_mutex); + _epggrab_module_run(mod, NULL, NULL); return ret; } /* * Advanced run - * - * TODO: might be nice to put each module run in another thread, in case - * they're scheduled at the same time? */ -time_t _epggrab_thread_advanced ( void ) +static time_t _epggrab_thread_advanced ( void ) { + time_t ret; epggrab_sched_t *s; /* Determine which to run */ - LIST_FOREACH(s, &epggrab_schedule, es_link) { - if ( cron_is_time(&s->cron) ) { - _epggrab_module_run(s->mod, s->opts); + time(&ret); + LIST_FOREACH(s, &epggrab_schedule, link) { + if ( cron_is_time(s->cron) ) { + _epggrab_module_run(s->mod, s->cmd, s->opts); + break; // TODO: can only run once else config may have changed } } - // TODO: make this driven off next time - // get cron to tell us when next call will run - return time(NULL) + 60; + return ret + 60; } /* * Thread */ -void* _epggrab_thread ( void* p ) +static void* _epggrab_thread ( void* p ) { int confver = 0; struct timespec ts; @@ -184,8 +457,10 @@ void* _epggrab_thread ( void* p ) /* Check for config change */ while ( confver == epggrab_confver ) { - if ( pthread_cond_timedwait(&epggrab_cond, &epggrab_mutex, &ts) == ETIMEDOUT ) break; + int err = pthread_cond_timedwait(&epggrab_cond, &epggrab_mutex, &ts); + if ( err == ETIMEDOUT ) break; } + confver = epggrab_confver; /* Run grabber */ ts.tv_sec = epggrab_advanced @@ -197,301 +472,59 @@ void* _epggrab_thread ( void* p ) } /* ************************************************************************** - * Module access + * Global Functions * *************************************************************************/ -epggrab_module_t* epggrab_module_find_by_name ( const char *name ) +/* + * Initialise + */ +void epggrab_init ( void ) { - epggrab_module_t *m; - LIST_FOREACH(m, &epggrab_modules, glink) { - if ( !strcmp(m->name(), name) ) return m; - } - return NULL; -} + /* Defaults */ + epggrab_advanced = 0; + epggrab_eit = 1; // on air grab enabled + epggrab_interval = 12 * 3600; // hours + epggrab_module = NULL; // disabled -/* ************************************************************************** - * Channel handling - * - * TODO: this is all a bit messy (too much indirection?) but it works! - * - * TODO: need to save mappings to disk - * - * TODO: probably want rules on what can be updated etc... - * *************************************************************************/ + /* Initialise modules */ + eit_init(&epggrab_modules); + xmltv_init(&epggrab_modules); + pyepg_init(&epggrab_modules); -static int _ch_id_cmp ( void *a, void *b ) -{ - return strcmp(((epggrab_channel_t*)a)->id, - ((epggrab_channel_t*)b)->id); -} - -static channel_t *_channel_find ( epggrab_channel_t *ch ) -{ - channel_t *ret = NULL; - if (ch->name) ret = channel_find_by_name(ch->name, 0, 0); - return ret; -} - -static int _channel_match ( epggrab_channel_t *egc, channel_t *ch ) -{ - /* Already linked */ - if ( egc->channel ) return 0; - - /* Name match */ - if ( !strcmp(ch->ch_name, egc->name) ) return 1; - - return 0; + /* Start 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); } void epggrab_channel_add ( channel_t *ch ) { epggrab_module_t *m; - LIST_FOREACH(m, &epggrab_modules, glink) { - if (m->ch_add) m->ch_add(ch); + LIST_FOREACH(m, &epggrab_modules, link) { + if (m->ch_add) + if (m->ch_add(m, ch) && m->ch_save) + m->ch_save(m); } } void epggrab_channel_rem ( channel_t *ch ) { epggrab_module_t *m; - LIST_FOREACH(m, &epggrab_modules, glink) { - if (m->ch_rem) m->ch_rem(ch); + LIST_FOREACH(m, &epggrab_modules, link) { + if (m->ch_rem) + if (m->ch_rem(m, ch) && m->ch_save) + m->ch_save(m); } } void epggrab_channel_mod ( channel_t *ch ) { epggrab_module_t *m; - LIST_FOREACH(m, &epggrab_modules, glink) { - if (m->ch_mod) m->ch_mod(ch); + LIST_FOREACH(m, &epggrab_modules, link) { + if (m->ch_mod) + if (m->ch_mod(m, ch) && m->ch_save) + m->ch_save(m); } } - -void epggrab_module_channels_load - ( const char *path, epggrab_channel_tree_t *tree ) -{ - uint32_t chid; - htsmsg_t *m; - htsmsg_field_t *f; - epggrab_channel_t *ec; - channel_t *ch; - - if ((m = hts_settings_load(path))) { - HTSMSG_FOREACH(f, m) { - if ( !htsmsg_get_u32(m, f->hmf_name, &chid) ) { - ch = channel_find_by_identifier(chid); - if (ch) { - ec = calloc(1, sizeof(epggrab_channel_t)); - ec->id = strdup(f->hmf_name); - ec->channel = ch; - assert(RB_INSERT_SORTED(tree, ec, glink, _ch_id_cmp) == NULL); - } - } - } - } -} - -void epggrab_module_channels_save - ( const char *path, epggrab_channel_tree_t *tree ) -{ - epggrab_channel_t *c; - htsmsg_t *m = htsmsg_create_map(); - RB_FOREACH(c, tree, glink) { - if (c->channel) htsmsg_add_u32(m, c->id, c->channel->ch_id); - } - hts_settings_save(m, path); -} - -epggrab_channel_t *epggrab_module_channel_create - ( epggrab_channel_tree_t *tree, epggrab_channel_t *iskel, int *save ) -{ - epggrab_channel_t *egc; - static epggrab_channel_t *skel = NULL; - if (!skel) skel = calloc(1, sizeof(epggrab_channel_t)); - skel->id = iskel->id; - skel->name = iskel->name; - egc = RB_INSERT_SORTED(tree, skel, glink, _ch_id_cmp); - if ( egc == NULL ) { - skel->id = strdup(skel->id); - skel->name = strdup(skel->name); - skel->channel = _channel_find(skel); - if ( skel->channel ) *save |= 1; - egc = skel; - skel = NULL; - } - return egc; -} - -epggrab_channel_t *epggrab_module_channel_find - ( epggrab_channel_tree_t *tree, const char *id ) -{ - epggrab_channel_t skel; - skel.id = (char*)id; - return RB_FIND(tree, &skel, glink, _ch_id_cmp); -} - -int epggrab_module_channel_add ( epggrab_channel_tree_t *tree, channel_t *ch ) -{ - int save = 0; - epggrab_channel_t *egc; - RB_FOREACH(egc, tree, glink) { - if (_channel_match(egc, ch) ) { - save = 1; - egc->channel = ch; - break; - } - } - return save; -} - -int epggrab_module_channel_rem ( epggrab_channel_tree_t *tree, channel_t *ch ) -{ - int save = 0; - epggrab_channel_t *egc; - RB_FOREACH(egc, tree, glink) { - if (egc->channel == ch) { - save = 1; - egc->channel = NULL; - break; - } - } - return save; -} - -int epggrab_module_channel_mod ( epggrab_channel_tree_t *tree, channel_t *ch ) -{ - return epggrab_module_channel_add(tree, ch); -} - -/* ************************************************************************** - * Configuration handling - * *************************************************************************/ - -void _epggrab_load ( void ) -{ - htsmsg_t *m, *s, *e, *c; - htsmsg_field_t *f; - const char *str; - epggrab_sched_t *es; - - /* No config */ - if ((m = hts_settings_load("epggrab/config")) == NULL) - return; - - /* Load settings */ - htsmsg_get_u32(m, "advanced", &epggrab_advanced); - htsmsg_get_u32(m, "eit", &epggrab_eit); - if ( !epggrab_advanced ) { - htsmsg_get_u32(m, "interval", &epggrab_interval); - str = htsmsg_get_str(m, "module"); - if (str) epggrab_module = epggrab_module_find_by_name(str); - } else { - if ((s = htsmsg_get_list(m, "schedule")) != NULL) { - HTSMSG_FOREACH(f, s) { - if ((e = htsmsg_get_map_by_field(f)) != NULL) { - es = calloc(1, sizeof(epggrab_sched_t)); - str = htsmsg_get_str(e, "mod"); - if (str) es->mod = epggrab_module_find_by_name(str); - str = htsmsg_get_str(e, "opts"); - if (str) es->opts = strdup(str); - c = htsmsg_get_map(e, "cron"); - if (f) cron_unpack(&es->cron, c); - LIST_INSERT_HEAD(&epggrab_schedule, es, es_link); - } - } - } - } - htsmsg_destroy(m); -} - -void _epggrab_save ( void ) -{ - htsmsg_t *m, *s, *e, *c; - epggrab_sched_t *es; - - /* Enable EIT */ - - /* Register */ - epggrab_confver++; - pthread_cond_signal(&epggrab_cond); - - /* Save */ - m = htsmsg_create_map(); - htsmsg_add_u32(m, "advanced", epggrab_advanced); - htsmsg_add_u32(m, "eit", epggrab_eit); - if ( !epggrab_advanced ) { - htsmsg_add_u32(m, "interval", epggrab_interval); - if ( epggrab_module ) - htsmsg_add_str(m, "module", epggrab_module->name()); - } else { - s = htsmsg_create_list(); - LIST_FOREACH(es, &epggrab_schedule, es_link) { - e = htsmsg_create_map(); - if ( es->mod ) htsmsg_add_str(e, "mod", es->mod->name()); - if ( es->opts ) htsmsg_add_str(e, "opts", es->opts); - c = htsmsg_create_map(); - cron_pack(&es->cron, c); - htsmsg_add_msg(e, "cron", c); - htsmsg_add_msg(s, NULL, e); - } - htsmsg_add_msg(m, "schedule", s); - } - hts_settings_save(m, "epggrab/config"); - htsmsg_destroy(m); -} - -void _epggrab_set_schedule ( int count, epggrab_sched_t *sched ) -{ - int i; - epggrab_sched_t *es; - - /* Remove existing */ - while ( !LIST_EMPTY(&epggrab_schedule) ) { - es = LIST_FIRST(&epggrab_schedule); - LIST_REMOVE(es, es_link); - if ( es->opts ) free(es->opts); - free(es); - } - - /* Create new */ - for ( i = 0; i < count; i++ ) { - es = calloc(1, sizeof(epggrab_sched_t)); - es->mod = sched[i].mod; - es->cron = sched[i].cron; - if ( sched[i].opts ) es->opts = strdup(sched[i].opts); - LIST_INSERT_HEAD(&epggrab_schedule, es, es_link); - } -} - -void epggrab_set_simple ( uint32_t interval, epggrab_module_t *mod ) -{ - /* Set config */ - lock_assert(&epggrab_mutex); - epggrab_advanced = 0; - epggrab_interval = interval; - epggrab_module = mod; - - /* Save */ - _epggrab_save(); -} - -void epggrab_set_advanced ( uint32_t count, epggrab_sched_t *sched ) -{ - /* Set config */ - lock_assert(&epggrab_mutex); - epggrab_advanced = 1; - _epggrab_set_schedule(count, sched); - - /* Save */ - _epggrab_save(); -} - -void epggrab_set_eit ( uint32_t eit ) -{ - /* Set config */ - lock_assert(&epggrab_mutex); - epggrab_eit = eit; - - /* Save */ - _epggrab_save(); -} diff --git a/src/epggrab.h b/src/epggrab.h index a06080fe..28a6b35a 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -3,14 +3,12 @@ #include #include "cron.h" +#include "channels.h" /* ************************************************************************** - * Type definitions + * Grabber Stats * *************************************************************************/ -/* - * Grab statistics - */ typedef struct epggrab_stats_part { int created; @@ -27,47 +25,106 @@ typedef struct epggrab_stats epggrab_stats_part_t broadcasts; } epggrab_stats_t; +/* ************************************************************************** + * Grabber Channels + * *************************************************************************/ + /* * Grab channel */ typedef struct epggrab_channel { + RB_ENTRY(epggrab_channel) link; ///< Global link + char *id; ///< Grabber's ID char *name; ///< Channel name struct channel *channel; ///< Mapped channel // TODO: I think we might need a list of channels! - RB_ENTRY(epggrab_channel) glink; ///< Global link } epggrab_channel_t; +/* + * Channel list + */ RB_HEAD(epggrab_channel_tree, epggrab_channel); typedef struct epggrab_channel_tree epggrab_channel_tree_t; +/* ************************************************************************** + * Grabber Modules + * *************************************************************************/ + +typedef struct epggrab_module epggrab_module_t; + /* * Grabber base class */ -typedef struct epggrab_module +struct epggrab_module { - const char* (*name) ( void ); - void (*enable) ( void ); - void (*disable) ( void ); - htsmsg_t* (*grab) ( const char *opts ); - int (*parse) ( htsmsg_t *data, epggrab_stats_t *stats ); - void (*ch_add) ( struct channel *ch ); - void (*ch_rem) ( struct channel *ch ); - void (*ch_mod) ( struct channel *ch ); + LIST_ENTRY(epggrab_module) link; ///< Global list link - LIST_ENTRY(epggrab_module) glink; ///< Global list link -} epggrab_module_t; + const char *id; ///< Module identifier + const char *name; ///< Module name (for display) + const char *path; ///< Module path (for fixed config) + const uint8_t simple; ///< Include in simple list + const uint8_t async; ///< Asynchronous (not schedulable) + epggrab_channel_tree_t *channels; ///< Channel list + + /* Enable/Disable for async */ + void (*enable) ( epggrab_module_t *m, uint8_t e ); + + /* Synchronous grab&parse */ + htsmsg_t* (*grab) ( epggrab_module_t *m, + const char *c, const char *o ); + int (*parse) ( epggrab_module_t *m, + htsmsg_t *d, epggrab_stats_t *s ); + + /* Channel listings */ + int (*ch_add) ( epggrab_module_t *m, channel_t *ch ); + int (*ch_rem) ( epggrab_module_t *m, channel_t *ch ); + int (*ch_mod) ( epggrab_module_t *m, channel_t *ch ); + + /* Save any settings */ + void (*ch_save) ( epggrab_module_t *m ); +}; /* - * Schedule specification + * Default channel handlers + */ +void epggrab_module_channels_load ( epggrab_module_t *m ); +void epggrab_module_channels_save ( epggrab_module_t *m ); +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_module_t *m, const char *id ); + +/* + * Module list + */ +LIST_HEAD(epggrab_module_list, epggrab_module); +typedef struct epggrab_module_list epggrab_module_list_t; + +/* + * Access the Module list + */ +epggrab_module_t* epggrab_module_find_by_id ( const char *id ); +htsmsg_t* epggrab_module_list ( void ); + +/* ************************************************************************** + * Configuration + * *************************************************************************/ + +/* + * Schedule specification (for advanced config) */ typedef struct epggrab_sched { - LIST_ENTRY(epggrab_sched) es_link; - cron_t cron; ///< Cron definition - epggrab_module_t *mod; ///< Module - char *opts; ///< Extra (advanced) options + LIST_ENTRY(epggrab_sched) link; + cron_t *cron; ///< Cron definition + epggrab_module_t *mod; ///< Module + char *cmd; ///< Command + char *opts; ///< Extra (advanced) options } epggrab_sched_t; /* @@ -76,37 +133,35 @@ typedef struct epggrab_sched LIST_HEAD(epggrab_sched_list, epggrab_sched); typedef struct epggrab_sched_list epggrab_sched_list_t; -/* ************************************************************************** - * Variables - * *************************************************************************/ +/* + * Configuration + */ +extern pthread_mutex_t epggrab_mutex; +extern uint32_t epggrab_advanced; +extern uint32_t epggrab_eitenabled; +extern uint32_t epggrab_interval; +extern epggrab_module_t* epggrab_module; +extern epggrab_sched_list_t epggrab_schedule; + +htsmsg_t *epggrab_get_schedule ( void ); /* - * Lock this if accessing configuration + * Update */ -pthread_mutex_t epggrab_mutex; +int epggrab_set_advanced ( uint32_t advanced ); +int epggrab_set_eitenabled ( uint32_t eitenabled ); +int epggrab_set_interval ( uint32_t interval ); +int epggrab_set_module ( epggrab_module_t *module ); +int epggrab_set_module_by_id ( const char *id ); +int epggrab_set_schedule ( htsmsg_t *sched ); +void epggrab_save ( void ); /* ************************************************************************** - * Functions + * Global Functions * *************************************************************************/ -/* - * Setup - */ void epggrab_init ( void ); -/* - * Access the list of supported modules - */ -//epggrab_module_t** epggrab_module_list ( void ); -epggrab_module_t* epggrab_module_find_by_name ( const char *name ); - -/* - * Set configuration - */ -void epggrab_set_simple ( uint32_t interval, epggrab_module_t* mod ); -void epggrab_set_advanced ( uint32_t count, epggrab_sched_t* sched ); -void epggrab_set_eit ( uint32_t eit ); - /* * Channel handling */ @@ -114,16 +169,4 @@ void epggrab_channel_add ( struct channel *ch ); void epggrab_channel_rem ( struct channel *ch ); void epggrab_channel_mod ( struct channel *ch ); -/* - * Module specific channel handling - */ -void epggrab_module_channels_load ( const char *path, epggrab_channel_tree_t *tree ); -void epggrab_module_channels_save ( const char *path, epggrab_channel_tree_t *tree ); -int epggrab_module_channel_add ( epggrab_channel_tree_t *tree, struct channel *ch ); -int epggrab_module_channel_rem ( epggrab_channel_tree_t *tree, struct channel *ch ); -int epggrab_module_channel_mod ( epggrab_channel_tree_t *tree, struct channel *ch ); - -epggrab_channel_t *epggrab_module_channel_create ( epggrab_channel_tree_t *tree, epggrab_channel_t *ch, int *save ); -epggrab_channel_t *epggrab_module_channel_find ( epggrab_channel_tree_t *tree, const char *id ); - #endif /* __EPGGRAB_H__ */ diff --git a/src/epggrab/eit.c b/src/epggrab/eit.c index 6fea4c9d..ba5368df 100644 --- a/src/epggrab/eit.c +++ b/src/epggrab/eit.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include "tvheadend.h" #include "channels.h" #include "epg.h" @@ -25,13 +26,6 @@ * Module Setup * ***********************************************************************/ -static epggrab_module_t eit_module; - -static const char* _eit_name ( void ) -{ - return "eit"; -} - static void _eit_episode_uri ( char *uri, const char *title, const char *summary ) { @@ -90,14 +84,11 @@ void eit_callback ( channel_t *ch, int id, time_t start, time_t stop, } } -epggrab_module_t* eit_init ( void ) +epggrab_module_t _eit_mod; +void eit_init ( epggrab_module_list_t *list ) { - // Note: the EIT grabber is very different to the others, in that - // its asynchronous based on DVB data stream - eit_module.enable = NULL; - eit_module.disable = NULL; - eit_module.grab = NULL; - eit_module.parse = NULL; - eit_module.name = _eit_name; - return &eit_module; + _eit_mod.id = strdup("eit"); + _eit_mod.name = strdup("EIT On-Air Grabber"); + *((uint8_t*)&_eit_mod.async) = 1; + LIST_INSERT_HEAD(list, &_eit_mod, link); } diff --git a/src/epggrab/eit.h b/src/epggrab/eit.h index 3b0870c4..c30341e5 100644 --- a/src/epggrab/eit.h +++ b/src/epggrab/eit.h @@ -21,7 +21,7 @@ #include "epggrab.h" -epggrab_module_t *eit_init ( void ); +void eit_init ( epggrab_module_list_t *list ); void eit_callback ( struct channel *ch, int id, time_t start, time_t stop, const char *title, const char *desc, diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c index 538b4a26..2155bfde 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -29,60 +29,19 @@ #include "channels.h" epggrab_channel_tree_t _pyepg_channels; - -/* ************************************************************************** - * Config/Load save - * *************************************************************************/ - -static void _pyepg_load ( void ) -{ - epggrab_module_channels_load("epggrab/pyepg/channels", &_pyepg_channels); -} - -static void _pyepg_save ( void ) -{ - epggrab_module_channels_save("epggrab/pyepg/channels", &_pyepg_channels); -} - -/* ************************************************************************** - * Channels - * *************************************************************************/ - +epggrab_module_t _pyepg_sync; +epggrab_module_t _pyepg_async; static channel_t *_pyepg_channel_create ( epggrab_channel_t *skel, int *save ) { - epggrab_channel_t *ch - = epggrab_module_channel_create(&_pyepg_channels, skel, save); - if (ch) return ch->channel; return NULL; } static channel_t *_pyepg_channel_find ( const char *id ) { - epggrab_channel_t *ch - = epggrab_module_channel_find(&_pyepg_channels, id); - if (ch) return ch->channel; return NULL; } -static void _pyepg_channel_add ( channel_t *ch ) -{ - if ( epggrab_module_channel_add(&_pyepg_channels, ch) ) - _pyepg_save(); -} - -static void _pyepg_channel_rem ( channel_t *ch ) -{ - if ( epggrab_module_channel_rem(&_pyepg_channels, ch) ) - _pyepg_save(); -} - -static void _pyepg_channel_mod ( channel_t *ch ) -{ - if ( epggrab_module_channel_mod(&_pyepg_channels, ch) ) - _pyepg_save(); -} - /* ************************************************************************** * Parsing * *************************************************************************/ @@ -397,16 +356,18 @@ static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats ) save |= _pyepg_parse_schedule(htsmsg_get_map_by_field(f), stats); } } +#if 0 save |= chsave; - if (chsave) _pyepg_save(); + if (chsave) pyepg_save(); +#endif return save; } -static int _pyepg_parse ( htsmsg_t *data, epggrab_stats_t *stats ) +static int _pyepg_parse + ( epggrab_module_t *mod, htsmsg_t *data, epggrab_stats_t *stats ) { htsmsg_t *tags, *epg; - epggrab_module_t *mod; if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; @@ -416,8 +377,8 @@ static int _pyepg_parse ( htsmsg_t *data, epggrab_stats_t *stats ) /* XMLTV format */ if ((epg = htsmsg_get_map(tags, "tv")) != NULL) { - mod = epggrab_module_find_by_name("xmltv"); - if (mod) return mod->parse(epg, stats); + mod = epggrab_module_find_by_id("xmltv_sync"); + if (mod) return mod->parse(mod, epg, stats); } return 0; @@ -427,14 +388,12 @@ static int _pyepg_parse ( htsmsg_t *data, epggrab_stats_t *stats ) * Module Setup * ***********************************************************************/ -static epggrab_module_t pyepg_module; - -static const char* _pyepg_name ( void ) +static void _pyepg_enable ( epggrab_module_t *mod, uint8_t e ) { - return "pyepg"; } -static htsmsg_t* _pyepg_grab ( const char *iopts ) + +static htsmsg_t* _pyepg_grab ( epggrab_module_t *mod, const char *icmd, const char *iopts ) { int i, outlen; char *outbuf; @@ -479,17 +438,29 @@ static htsmsg_t* _pyepg_grab ( const char *iopts ) return ret; } -epggrab_module_t* pyepg_init ( void ) +void pyepg_init ( epggrab_module_list_t *list ) { - pyepg_module.enable = NULL; - pyepg_module.disable = NULL; - pyepg_module.grab = _pyepg_grab; - pyepg_module.parse = _pyepg_parse; - pyepg_module.name = _pyepg_name; - pyepg_module.ch_add = _pyepg_channel_add; - pyepg_module.ch_rem = _pyepg_channel_rem; - pyepg_module.ch_mod = _pyepg_channel_mod; - _pyepg_load(); - return &pyepg_module; + /* Common routines */ + _pyepg_sync.channels = _pyepg_async.channels = &_pyepg_channels; + _pyepg_sync.ch_save = _pyepg_async.ch_save = epggrab_module_channels_save; + _pyepg_sync.ch_add = _pyepg_async.ch_add = epggrab_module_channel_add; + _pyepg_sync.ch_rem = _pyepg_async.ch_rem = epggrab_module_channel_rem; + _pyepg_sync.ch_mod = _pyepg_async.ch_mod = epggrab_module_channel_mod; + _pyepg_sync.name = _pyepg_async.name = strdup("PyEPG"); + + /* Sync */ + _pyepg_sync.id = strdup("pyepg_sync"); + _pyepg_sync.path = strdup("/usr/bin/pyepg"); + _pyepg_sync.grab = _pyepg_grab; + _pyepg_sync.parse = _pyepg_parse; + + /* Async */ + _pyepg_async.id = strdup("pyepg_async"); + _pyepg_async.enable = _pyepg_enable; + *((uint8_t*)&_pyepg_async.async) = 1; + + /* Add to list */ + LIST_INSERT_HEAD(list, &_pyepg_sync, link); + LIST_INSERT_HEAD(list, &_pyepg_async, link); } diff --git a/src/epggrab/pyepg.h b/src/epggrab/pyepg.h index 716baf43..a005c165 100644 --- a/src/epggrab/pyepg.h +++ b/src/epggrab/pyepg.h @@ -21,6 +21,6 @@ #include "epggrab.h" -epggrab_module_t* pyepg_init ( void ); +void pyepg_init ( epggrab_module_list_t *list ); #endif diff --git a/src/epggrab/xmltv.c b/src/epggrab/xmltv.c index 970e8d77..f96d982b 100644 --- a/src/epggrab/xmltv.c +++ b/src/epggrab/xmltv.c @@ -35,6 +35,8 @@ #include "xmltv.h" #include "spawn.h" +#define XMLTV_FIND_GRABBERS "/usr/bin/tv_find_grabbers" + /* ************************************************************************** * Parsing * *************************************************************************/ @@ -381,7 +383,51 @@ static int _xmltv_parse ( htsmsg_t *data, epggrab_stats_t *stats ) return _xmltv_parse_tv(tv, stats); } +#endif +/* ************************************************************************ + * Config + * ***********************************************************************/ + +#if 0 +static htsmsg_t *xmltv_grabber_list ( void ) +{ + size_t i, outlen, p, n; + char *outbuf; + char errbuf[100]; + htsmsg_t *e = NULL, *array; + + /* Load data */ + outlen = spawn_and_store_stdout(XMLTV_FIND_GRABBERS, NULL, &outbuf); + if ( outlen < 1 ) { + tvhlog(LOG_ERR, "xmltv", "%s failed [%s]", XMLTV_FIND_GRABBERS, errbuf); + return NULL; + } + + /* Process */ + array = htsmsg_create_list(); + p = n = i = 0; + while ( i < outlen ) { + if ( outbuf[i] == '\n' || outbuf[i] == '\0' ) { + outbuf[i] = '\0'; + e = htsmsg_create_map(); + htsmsg_add_str(e, "path", &outbuf[p]); + htsmsg_add_str(e, "name", &outbuf[n]); + htsmsg_add_msg(array, NULL, e); + p = n = i + 1; + } else if ( outbuf[i] == '|' ) { + outbuf[i] = '\0'; + n = i + 1; + } + i++; + } + free(outbuf); + + return array; +} +#endif + +#if TODO_XMLTV /* ************************************************************************ * Module Setup * ***********************************************************************/ @@ -401,7 +447,6 @@ static htsmsg_t* _xmltv_grab ( const char *iopts ) size_t outlen; char *outbuf; char errbuf[100]; - const char *cmd = "/home/aps/tmp/epg.sh";//usr/bin/tv_grab_uk_rt"; /* Debug */ tvhlog(LOG_DEBUG, "xmltv", "grab %s", cmd); diff --git a/src/epggrab/xmltv.h b/src/epggrab/xmltv.h index bef9e6b9..b4637a1a 100644 --- a/src/epggrab/xmltv.h +++ b/src/epggrab/xmltv.h @@ -19,8 +19,9 @@ #ifndef EPGGRAB_XMLTV_H #define EPGGRAB_XMLTV_H +#include "htsmsg.h" #include "epggrab.h" -epggrab_module_t *xmltv_init ( void ); +void xmltv_init ( epggrab_module_list_t *list ); #endif