From 190e2efc9d26bd2b833aba274e57b3b311be1ec6 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 May 2012 13:57:43 +0100 Subject: [PATCH 01/10] Add a few extra bits of info to the epg ui code. --- src/webui/extjs.c | 2 ++ src/webui/static/app/epg.js | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/webui/extjs.c b/src/webui/extjs.c index c4e4c54d..7e6c5145 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -722,6 +722,8 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) if(ee->title != NULL) htsmsg_add_str(m, "title", ee->title); + if(ee->subtitle) + htsmsg_add_str(m, "subtitle", ee->subtitle); if(ee->description != NULL) htsmsg_add_str(m, "description", ee->description); diff --git a/src/webui/static/app/epg.js b/src/webui/static/app/epg.js index 68eaff4f..2c8e5e8c 100644 --- a/src/webui/static/app/epg.js +++ b/src/webui/static/app/epg.js @@ -14,7 +14,9 @@ tvheadend.epgDetails = function(event) { if(event.chicon != null && event.chicon.length > 0) content += ''; - content += '
' + event.title + '
'; + content += '
' + event.title; + if (event.subtitle) content += " : " + event.subtitle; + content += '
'; content += '
' + event.episode + '
'; content += '
' + event.description + '
'; @@ -120,7 +122,7 @@ tvheadend.epgDetails = function(event) { html += '
'; if (ee.episode) html += ee.episode + '   '; html += ee.title; - if (ee.subtitle) html += ':' + ee.subtitle + if (ee.subtitle) html += ' : ' + ee.subtitle html += '
'; } } @@ -180,11 +182,9 @@ tvheadend.epg = function() { {name: 'channel'}, {name: 'channelid'}, {name: 'title'}, + {name: 'subtitle'}, {name: 'episode'}, {name: 'description'}, - {name: 'ext_desc'}, - {name: 'ext_item'}, - {name: 'ext_text'}, {name: 'chicon'}, {name: 'start', type: 'date', dateFormat: 'U' /* unix time */}, {name: 'end', type: 'date', dateFormat: 'U' /* unix time */}, @@ -242,6 +242,12 @@ tvheadend.epg = function() { header: "Title", dataIndex: 'title', renderer: renderText + },{ + width: 250, + id:'subtitle', + header: "SubTitle", + dataIndex: 'subtitle', + renderer: renderText },{ width: 100, id:'episode', From 01a8b574ec5a5a6ddd28ef23028bcd312eaf90e9 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 31 May 2012 15:58:41 +0100 Subject: [PATCH 02/10] Add some initial code to load/save grab module channel mappings. --- src/epggrab.c | 64 +++++++++++++++++++++++++++++++++++++++------ src/epggrab.h | 10 ++++--- src/epggrab/pyepg.c | 54 +++++++++++++++++++++++++++++--------- 3 files changed, 104 insertions(+), 24 deletions(-) diff --git a/src/epggrab.c b/src/epggrab.c index 8762f3cb..b1107b0c 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "htsmsg.h" #include "settings.h" #include "tvheadend.h" @@ -41,6 +42,8 @@ static void _epggrab_set_schedule ( int, epggrab_sched_t* ); */ void epggrab_init ( void ) { + epggrab_module_t *m; + /* Defaults */ epggrab_advanced = 0; epggrab_eit = 1; // on air grab enabled @@ -48,11 +51,14 @@ void epggrab_init ( void ) epggrab_module = NULL; // disabled /* Initialise modules */ - LIST_INSERT_HEAD(&epggrab_modules, eit_init(), glink); + m = eit_init(); + LIST_INSERT_HEAD(&epggrab_modules, m, glink); #if TODO_XMLTV_SUPPORT - LIST_INSERT_HEAD(&epggrab_modules, xmltv_init(), glink); + m = xmltv_init(); + LIST_INSERT_HEAD(&epggrab_modules, m, glink); #endif - LIST_INSERT_HEAD(&epggrab_modules, pyepg_init(), glink); + m = pyepg_init(); + LIST_INSERT_HEAD(&epggrab_modules, m, glink); /* Start thread */ pthread_t tid; @@ -261,8 +267,43 @@ void epggrab_channel_mod ( channel_t *ch ) } } +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 ) + ( epggrab_channel_tree_t *tree, epggrab_channel_t *iskel, int *save ) { epggrab_channel_t *egc; static epggrab_channel_t *skel = NULL; @@ -274,6 +315,7 @@ epggrab_channel_t *epggrab_module_channel_create skel->id = strdup(skel->id); skel->name = strdup(skel->name); skel->channel = _channel_find(skel); + if ( skel->channel ) *save |= 1; egc = skel; skel = NULL; } @@ -288,31 +330,37 @@ epggrab_channel_t *epggrab_module_channel_find return RB_FIND(tree, &skel, glink, _ch_id_cmp); } -void epggrab_module_channel_add ( epggrab_channel_tree_t *tree, channel_t *ch ) +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; } -void epggrab_module_channel_rem ( epggrab_channel_tree_t *tree, channel_t *ch ) +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; } -void epggrab_module_channel_mod ( epggrab_channel_tree_t *tree, channel_t *ch ) +int epggrab_module_channel_mod ( epggrab_channel_tree_t *tree, channel_t *ch ) { - epggrab_module_channel_add(tree, ch); + return epggrab_module_channel_add(tree, ch); } /* ************************************************************************** diff --git a/src/epggrab.h b/src/epggrab.h index dbf961b3..a06080fe 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -117,11 +117,13 @@ void epggrab_channel_mod ( struct channel *ch ); /* * Module specific channel handling */ -void epggrab_module_channel_add ( epggrab_channel_tree_t *tree, struct channel *ch ); -void epggrab_module_channel_rem ( epggrab_channel_tree_t *tree, struct channel *ch ); -void epggrab_module_channel_mod ( epggrab_channel_tree_t *tree, struct channel *ch ); +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 ); +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/pyepg.c b/src/epggrab/pyepg.c index 6c52a911..538b4a26 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -28,16 +28,31 @@ #include "epggrab/pyepg.h" #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_channel_tree_t _pyepg_channels; -static channel_t *_pyepg_channel_create ( epggrab_channel_t *skel ) +static channel_t *_pyepg_channel_create ( epggrab_channel_t *skel, int *save ) { epggrab_channel_t *ch - = epggrab_module_channel_create(&_pyepg_channels, skel); + = epggrab_module_channel_create(&_pyepg_channels, skel, save); if (ch) return ch->channel; return NULL; } @@ -52,17 +67,20 @@ static channel_t *_pyepg_channel_find ( const char *id ) static void _pyepg_channel_add ( channel_t *ch ) { - epggrab_module_channel_add(&_pyepg_channels, ch); + if ( epggrab_module_channel_add(&_pyepg_channels, ch) ) + _pyepg_save(); } static void _pyepg_channel_rem ( channel_t *ch ) { - epggrab_module_channel_rem(&_pyepg_channels, ch); + if ( epggrab_module_channel_rem(&_pyepg_channels, ch) ) + _pyepg_save(); } static void _pyepg_channel_mod ( channel_t *ch ) { - epggrab_module_channel_mod(&_pyepg_channels, ch); + if ( epggrab_module_channel_mod(&_pyepg_channels, ch) ) + _pyepg_save(); } /* ************************************************************************** @@ -83,7 +101,7 @@ static int _pyepg_parse_time ( const char *str, time_t *out ) static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats ) { - int save = 0; + int save = 0, save2 = 0; htsmsg_t *attr, *tags; const char *icon; channel_t *ch; @@ -97,14 +115,23 @@ static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats ) skel.name = (char*)htsmsg_xml_get_cdata_str(tags, "name"); icon = htsmsg_xml_get_cdata_str(tags, "image"); - ch = _pyepg_channel_create(&skel); + ch = _pyepg_channel_create(&skel, &save2); + stats->channels.total++; + if (save2) { + stats->channels.created++; + save |= 1; + } /* Update values */ if (ch) { - // TODO: set the name - //if (skel.name) save |= channel_rename(ch, skel.name); + if (skel.name) { + if(!ch->ch_name || strcmp(ch->ch_name, skel.name)) { + save |= channel_rename(ch, skel.name); + } + } if (icon) channel_set_icon(ch, icon); } + if (save) stats->channels.modified++; return save; } @@ -351,7 +378,7 @@ static int _pyepg_parse_schedule ( htsmsg_t *data, epggrab_stats_t *stats ) static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats ) { - int save = 0; + int save = 0, chsave = 0; htsmsg_t *tags; htsmsg_field_t *f; @@ -359,7 +386,7 @@ static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats ) HTSMSG_FOREACH(f, tags) { if (strcmp(f->hmf_name, "channel") == 0 ) { - save |= _pyepg_parse_channel(htsmsg_get_map_by_field(f), stats); + chsave |= _pyepg_parse_channel(htsmsg_get_map_by_field(f), stats); } else if (strcmp(f->hmf_name, "brand") == 0 ) { save |= _pyepg_parse_brand(htsmsg_get_map_by_field(f), stats); } else if (strcmp(f->hmf_name, "series") == 0 ) { @@ -370,6 +397,8 @@ static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats ) save |= _pyepg_parse_schedule(htsmsg_get_map_by_field(f), stats); } } + save |= chsave; + if (chsave) _pyepg_save(); return save; } @@ -460,6 +489,7 @@ epggrab_module_t* pyepg_init ( void ) 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; } From 0d497457ac27ed199f418b76fb940fa08b7b07ac Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 1 Jun 2012 10:33:58 +0100 Subject: [PATCH 03/10] Added string processing capability to cron code. --- src/cron.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cron.h | 15 +++++++--- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/src/cron.c b/src/cron.c index 1bb6e553..dbd55834 100644 --- a/src/cron.c +++ b/src/cron.c @@ -1,5 +1,93 @@ #include "cron.h" #include +#include + +static int _field_to_string + ( char *out, uint64_t field, uint64_t mask, int offset ) +{ + int i, cnt = 0, first = 1, start = -1; + field &= mask; + + /* Star */ + if ( field == mask ) + return sprintf(out, "*"); + + for ( i = 0; i < 64; i++ ) { + int b = (field >> i) & 0x1; + if ( !b & start != -1 ) { + if (!first) + cnt += sprintf(out+cnt, ","); + first = 0; + cnt += sprintf(out+cnt, "%d", start + offset); + if (i != (start+1)) + cnt += sprintf(out+cnt, "-%d", (i - 1) + offset); + start = -1; + } else if ( b & start == -1 ) + start = i; + } + + return cnt; +} + +static int _field_from_string + ( const char *str, uint64_t *field, uint64_t mask, int offset ) +{ + int i = 0, j = 0, sn = -1, en = -1; + *field = 0; + while ( str[i] != '\0' ) { + if ( str[i] == '*' ) *field = mask; + else if ( str[i] == ',' || str[i] == ' ' ) { + sscanf(str+j, "%d", sn == -1 ? &sn : &en); + if (en == -1) en = sn; + while (sn <= en) { + *field |= (0x1LL << (sn - offset)); + sn++; + } + j = i+1; + sn = -1; + en = -1; + if (str[i] == ' ') break; + } else if ( str[i] == '-' ) { + sscanf(str+j, "%d", &sn); + j = i+1; + en = 64; + } + i++; + } + if (str[i] == ' ') i++; + *field &= mask; + return i; +} + +void cron_to_string ( cron_t *cron, char *str ) +{ + int i = 0; + i += _field_to_string(str+i, cron->min, CRON_MIN_MASK, 0); + i += sprintf(str+i, " "); + i += _field_to_string(str+i, cron->hour, CRON_HOUR_MASK, 0); + i += sprintf(str+i, " "); + i += _field_to_string(str+i, cron->dom, CRON_DOM_MASK, 1); + i += sprintf(str+i, " "); + i += _field_to_string(str+i, cron->mon, CRON_MON_MASK, 1); + i += sprintf(str+i, " "); + i += _field_to_string(str+i, cron->dow, CRON_DOW_MASK, 0); +} + +void cron_from_string ( cron_t *cron, const char *str ) +{ + int i = 0; + uint64_t u64; + i += _field_from_string(str+i, &u64, CRON_MIN_MASK, 0); + cron->min = u64; + i += _field_from_string(str+i, &u64, CRON_HOUR_MASK, 0); + cron->hour = (uint32_t)u64; + i += _field_from_string(str+i, &u64, CRON_DOM_MASK, 1); + cron->dom = (uint32_t)u64; + i += _field_from_string(str+i, &u64, CRON_MON_MASK, 1); + cron->mon = (uint16_t)u64; + i += _field_from_string(str+i, &u64, CRON_DOW_MASK, 0); + cron->dow = (uint8_t)u64; +} int cron_is_time ( cron_t *cron ) { diff --git a/src/cron.h b/src/cron.h index 9724c84d..0456b714 100644 --- a/src/cron.h +++ b/src/cron.h @@ -5,6 +5,12 @@ #include #include "htsmsg.h" +#define CRON_MIN_MASK 0x0FFFFFFFFFFFFFFFLL // 60 bits +#define CRON_HOUR_MASK 0x00FFFFFF // 24 bits +#define CRON_DOM_MASK 0x7FFFFFFF // 31 bits +#define CRON_MON_MASK 0x0FFF // 12 bits +#define CRON_DOW_MASK 0xFF // 8 bits (allow 0/7 for sunday) + typedef struct cron { uint64_t min; ///< Minutes @@ -14,9 +20,10 @@ typedef struct cron uint8_t dow; ///< Day of week } cron_t; -void cron_set_string ( cron_t *cron, const char *str ); -int cron_is_time ( cron_t *cron ); -void cron_pack ( cron_t *cron, htsmsg_t *msg ); -void cron_unpack ( cron_t *cron, htsmsg_t *msg ); +void cron_to_string ( cron_t *cron, char *str ); +void cron_from_string ( cron_t *cron, const char *str ); +int cron_is_time ( cron_t *cron ); +void cron_pack ( cron_t *cron, htsmsg_t *msg ); +void cron_unpack ( cron_t *cron, htsmsg_t *msg ); #endif /* __CRON_H__ */ From 473aced75fe92374cb8129bee5494f6e293798f9 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 1 Jun 2012 19:41:48 +0100 Subject: [PATCH 04/10] Some more updates to the cron system, simplified somewhat by storing string and converting to bit masks for running. --- src/cron.c | 199 +++++++++++++++++++++++++++++++++-------------------- src/cron.h | 22 +++--- 2 files changed, 134 insertions(+), 87 deletions(-) diff --git a/src/cron.c b/src/cron.c index dbd55834..efdd8c32 100644 --- a/src/cron.c +++ b/src/cron.c @@ -1,54 +1,48 @@ #include "cron.h" #include #include +#include +#include -static int _field_to_string - ( char *out, uint64_t field, uint64_t mask, int offset ) +typedef struct cron { - int i, cnt = 0, first = 1, start = -1; - field &= mask; + char *str; ///< String representation + uint64_t min; ///< Minutes + uint32_t hour; ///< Hours + uint32_t dom; ///< Day of month + uint16_t mon; ///< Month + uint8_t dow; ///< Day of week +} cron_t; - /* Star */ - if ( field == mask ) - return sprintf(out, "*"); - - for ( i = 0; i < 64; i++ ) { - int b = (field >> i) & 0x1; - if ( !b & start != -1 ) { - if (!first) - cnt += sprintf(out+cnt, ","); - first = 0; - cnt += sprintf(out+cnt, "%d", start + offset); - if (i != (start+1)) - cnt += sprintf(out+cnt, "-%d", (i - 1) + offset); - start = -1; - } else if ( b & start == -1 ) - start = i; - } - - return cnt; -} - -static int _field_from_string +static int _cron_parse_field ( const char *str, uint64_t *field, uint64_t mask, int offset ) { - int i = 0, j = 0, sn = -1, en = -1; + int i = 0, j = 0, sn = -1, en = -1, mn = -1; *field = 0; while ( str[i] != '\0' ) { - if ( str[i] == '*' ) *field = mask; - else if ( str[i] == ',' || str[i] == ' ' ) { - sscanf(str+j, "%d", sn == -1 ? &sn : &en); + if ( str[i] == '*' ) { + sn = 0; + en = 64; + j = -1; + } else if ( str[i] == ',' || str[i] == ' ' ) { + if (j >= 0) + sscanf(str+j, "%d", en == -1 ? (sn == -1 ? &sn : &en) : &mn); if (en == -1) en = sn; + if (mn == -1) mn = 1; while (sn <= en) { - *field |= (0x1LL << (sn - offset)); + if ( (sn % mn) == 0 ) + *field |= (0x1LL << (sn - offset)); sn++; } j = i+1; - sn = -1; - en = -1; + sn = en = mn = -1; if (str[i] == ' ') break; + } else if ( str[i] == '/' ) { + if (j >= 0) sscanf(str+j, "%d", sn == -1 ? &sn : &en); + j = i+1; + mn = 1; } else if ( str[i] == '-' ) { - sscanf(str+j, "%d", &sn); + if (j >= 0) sscanf(str+j, "%d", &sn); j = i+1; en = 64; } @@ -59,34 +53,74 @@ static int _field_from_string return i; } -void cron_to_string ( cron_t *cron, char *str ) -{ - int i = 0; - i += _field_to_string(str+i, cron->min, CRON_MIN_MASK, 0); - i += sprintf(str+i, " "); - i += _field_to_string(str+i, cron->hour, CRON_HOUR_MASK, 0); - i += sprintf(str+i, " "); - i += _field_to_string(str+i, cron->dom, CRON_DOM_MASK, 1); - i += sprintf(str+i, " "); - i += _field_to_string(str+i, cron->mon, CRON_MON_MASK, 1); - i += sprintf(str+i, " "); - i += _field_to_string(str+i, cron->dow, CRON_DOW_MASK, 0); -} - -void cron_from_string ( cron_t *cron, const char *str ) +static int _cron_parse ( cron_t *cron, const char *str ) { int i = 0; uint64_t u64; - i += _field_from_string(str+i, &u64, CRON_MIN_MASK, 0); - cron->min = u64; - i += _field_from_string(str+i, &u64, CRON_HOUR_MASK, 0); - cron->hour = (uint32_t)u64; - i += _field_from_string(str+i, &u64, CRON_DOM_MASK, 1); - cron->dom = (uint32_t)u64; - i += _field_from_string(str+i, &u64, CRON_MON_MASK, 1); - cron->mon = (uint16_t)u64; - i += _field_from_string(str+i, &u64, CRON_DOW_MASK, 0); - cron->dow = (uint8_t)u64; + + /* daily */ + if ( !strcmp(str, "@daily") ) { + cron->min = 1; + cron->hour = 1; + cron->dom = CRON_DOM_MASK; + cron->mon = CRON_MON_MASK; + cron->dow = CRON_DOW_MASK; + + /* hourly */ + } else if ( !strcmp(str, "@hourly") ) { + cron->min = 1; + cron->hour = CRON_HOUR_MASK; + cron->dom = CRON_DOM_MASK; + cron->mon = CRON_MON_MASK; + cron->dow = CRON_DOW_MASK; + + /* standard */ + } else { + i += _cron_parse_field(str+i, &u64, CRON_MIN_MASK, 0); + cron->min = u64; + i += _cron_parse_field(str+i, &u64, CRON_HOUR_MASK, 0); + cron->hour = (uint32_t)u64; + i += _cron_parse_field(str+i, &u64, CRON_DOM_MASK, 1); + cron->dom = (uint32_t)u64; + i += _cron_parse_field(str+i, &u64, CRON_MON_MASK, 1); + cron->mon = (uint16_t)u64; + i += _cron_parse_field(str+i, &u64, CRON_DOW_MASK, 0); + cron->dow = (uint8_t)u64; + } + + return 1; // TODO: do proper validation +} + +cron_t *cron_create ( void ) +{ + return calloc(1, sizeof(cron_t)); +} + +void cron_destroy ( cron_t *cron ) +{ + if (cron->str) free(cron->str); + free(cron); +} + +const char *cron_get_string ( cron_t *cron ) +{ + return cron->str; +} + +int cron_set_string ( cron_t *cron, const char *str ) +{ + int save = 0; + cron_t tmp; + if (!cron->str || !strcmp(cron->str, str)) { + tmp.str = (char*)str; + if (_cron_parse(&tmp, str)) { + if (cron->str) free(cron->str); + *cron = tmp; + cron->str = strdup(str); + save = 1; + } + } + return save; } int cron_is_time ( cron_t *cron ) @@ -109,23 +143,40 @@ int cron_is_time ( cron_t *cron ) return ret; } -void cron_pack ( cron_t *cron, htsmsg_t *msg ) +// TODO: do proper search for next time +time_t cron_next ( cron_t *cron ) { - htsmsg_add_u64(msg, "min", cron->min); - htsmsg_add_u32(msg, "hour", cron->hour); - htsmsg_add_u32(msg, "dom", cron->dom); - htsmsg_add_u32(msg, "mon", cron->mon); - htsmsg_add_u32(msg, "dow", cron->dow); + time_t now; + time(&now); + now += 60; + return (now / 60) * 60; // round to start of minute } -void cron_unpack ( cron_t *cron, htsmsg_t *msg ) + +void cron_serialize ( cron_t *cron, htsmsg_t *msg ) { - uint32_t u32 = 0; - htsmsg_get_u64(msg, "min", &cron->min); - htsmsg_get_u32(msg, "hour", &cron->hour); - htsmsg_get_u32(msg, "dom", &cron->dom); - htsmsg_get_u32(msg, "mon", &u32); - cron->mon = (uint16_t)u32; - htsmsg_get_u32(msg, "dow", &u32); - cron->dow = (uint8_t)u32; + htsmsg_add_str(msg, "cron", cron->str); } + +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; +} + +#if 0 +int main ( int argc, char **argv ) +{ + cron_t *c = cron_create(); + cron_set_string(c, argv[1]); + printf("min = 0x%016lx\n", c->min); + printf("hour = 0x%06x\n", c->hour); + printf("dom = 0x%08x\n", c->dom); + printf("mon = 0x%03hx\n", c->mon); + printf("dow = 0x%0hhx\n", c->dow); + cron_destroy(c); +} +#endif diff --git a/src/cron.h b/src/cron.h index 0456b714..f4d749cc 100644 --- a/src/cron.h +++ b/src/cron.h @@ -11,19 +11,15 @@ #define CRON_MON_MASK 0x0FFF // 12 bits #define CRON_DOW_MASK 0xFF // 8 bits (allow 0/7 for sunday) -typedef struct cron -{ - uint64_t min; ///< Minutes - uint32_t hour; ///< Hours - uint32_t dom; ///< Day of month - uint16_t mon; ///< Month - uint8_t dow; ///< Day of week -} cron_t; +typedef struct cron cron_t; -void cron_to_string ( cron_t *cron, char *str ); -void cron_from_string ( cron_t *cron, const char *str ); -int cron_is_time ( cron_t *cron ); -void cron_pack ( cron_t *cron, htsmsg_t *msg ); -void cron_unpack ( cron_t *cron, htsmsg_t *msg ); +cron_t *cron_create ( void ); +void cron_destroy ( cron_t *cron ); +const char *cron_get_string ( cron_t *cron ); +int cron_set_string ( cron_t *cron, const char *str ); +int cron_is_time ( cron_t *cron ); +time_t cron_next ( cron_t *cron ); +void cron_serialize ( cron_t *cron, htsmsg_t *msg ); +cron_t *cron_deserialize ( htsmsg_t *msg ); #endif /* __CRON_H__ */ From cf360abca91c3ab25e359e641ef61bb61ee64294 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 1 Jun 2012 22:17:47 +0100 Subject: [PATCH 05/10] 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 From 9b5486f01a5496327a1722e4c6d5fe0789a6255d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sun, 3 Jun 2012 23:12:15 +0100 Subject: [PATCH 06/10] Some further updates (simplfications) to the epggrab framework. --- src/epggrab.c | 5 +- src/epggrab.h | 17 +++++-- src/epggrab/eit.c | 20 ++++++-- src/epggrab/pyepg.c | 105 +++++++++++---------------------------- src/epggrab/xmltv.c | 117 ++++++++++++++++++++------------------------ 5 files changed, 111 insertions(+), 153 deletions(-) diff --git a/src/epggrab.c b/src/epggrab.c index c8a5da95..9cb3e609 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -66,13 +66,12 @@ void epggrab_module_channels_load ( epggrab_module_t *mod ) } } -void epggrab_module_channels_save ( epggrab_module_t *mod ) +void epggrab_module_channels_save + ( epggrab_module_t *mod, const char *path ) { - 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); } diff --git a/src/epggrab.h b/src/epggrab.h index 28a6b35a..50b9887b 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -54,6 +54,14 @@ typedef struct epggrab_channel_tree epggrab_channel_tree_t; typedef struct epggrab_module epggrab_module_t; +/* + * Grabber flags + */ +#define EPGGRAB_MODULE_SYNC 0x1 +#define EPGGRAB_MODULE_ASYNC 0x2 +#define EPGGRAB_MODULE_SIMPLE 0x4 +#define EPGGRAB_MODULE_ADVANCED 0x8 + /* * Grabber base class */ @@ -64,8 +72,7 @@ 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 simple; ///< Include in simple list - const uint8_t async; ///< Asynchronous (not schedulable) + const uint8_t flags; ///< Mode flags epggrab_channel_tree_t *channels; ///< Channel list /* Enable/Disable for async */ @@ -87,10 +94,12 @@ struct epggrab_module }; /* - * Default channel handlers + * Default module functions */ +void epggrab_module_enable ( epggrab_module_t *m, uint8_t e ); +htsmsg_t *epggrab_module_grab ( epggrab_module_t *m, const char *cmd, const char *opts ); void epggrab_module_channels_load ( epggrab_module_t *m ); -void epggrab_module_channels_save ( 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 ); diff --git a/src/epggrab/eit.c b/src/epggrab/eit.c index ba5368df..faf811ea 100644 --- a/src/epggrab/eit.c +++ b/src/epggrab/eit.c @@ -84,11 +84,23 @@ void eit_callback ( channel_t *ch, int id, time_t start, time_t stop, } } -epggrab_module_t _eit_mod; +/* ************************************************************************ + * Module Setup + * ***********************************************************************/ + +static epggrab_module_t _eit_mod; +static uint8_t _eit_enabled; + +static void _eit_enable ( epggrab_module_t *mod, uint8_t e ) +{ + _eit_enabled = e; +} + 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.async) = 1; + _eit_mod.id = strdup("eit"); + _eit_mod.name = strdup("EIT: On-Air Grabber"); + *((uint8_t*)&_eit_mod.flags) = EPGGRAB_MODULE_ASYNC; + _eit_mod.enable = _eit_enable; LIST_INSERT_HEAD(list, &_eit_mod, link); } diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c index 2155bfde..088d3a56 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -28,9 +28,6 @@ #include "epggrab/pyepg.h" #include "channels.h" -epggrab_channel_tree_t _pyepg_channels; -epggrab_module_t _pyepg_sync; -epggrab_module_t _pyepg_async; static channel_t *_pyepg_channel_create ( epggrab_channel_t *skel, int *save ) { @@ -364,6 +361,19 @@ static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats ) return save; } + +/* ************************************************************************ + * Module Setup + * ***********************************************************************/ + +epggrab_channel_tree_t _pyepg_channels; +epggrab_module_t _pyepg_module; + +static void _pyepg_save ( epggrab_module_t *mod ) +{ + epggrab_module_channels_save(mod, "epggrab/pyepg/channels"); +} + static int _pyepg_parse ( epggrab_module_t *mod, htsmsg_t *data, epggrab_stats_t *stats ) { @@ -384,83 +394,24 @@ static int _pyepg_parse return 0; } -/* ************************************************************************ - * Module Setup - * ***********************************************************************/ - -static void _pyepg_enable ( epggrab_module_t *mod, uint8_t e ) -{ -} - - -static htsmsg_t* _pyepg_grab ( epggrab_module_t *mod, const char *icmd, const char *iopts ) -{ - int i, outlen; - char *outbuf; - char errbuf[100]; - const char *argv[32]; // 32 args max! - char *toksave, *tok; - char *opts = NULL; - htsmsg_t *ret; - - /* TODO: do something (much) better! */ - if (iopts) opts = strdup(iopts); - i = 1; - argv[0] = "/usr/bin/pyepg"; - if ( opts ) { - tok = strtok_r(opts, " ", &toksave); - while ( tok != NULL ) { - argv[i++] = tok; - tok = strtok_r(NULL, " ", &toksave); - } - } - argv[i] = NULL; - - /* Debug */ - tvhlog(LOG_DEBUG, "pyepg", "grab %s %s", argv[0], iopts ? iopts : ""); - - /* Grab */ -#if 0 - outlen = spawn_and_store_stdout(argv[0], (char *const*)argv, &outbuf); -#else - outlen = spawn_and_store_stdout("/home/aps/tmp/epg.sh", NULL, &outbuf); -#endif - free(opts); - if ( outlen < 1 ) { - tvhlog(LOG_ERR, "pyepg", "no output detected"); - return NULL; - } - - /* Extract */ - ret = htsmsg_xml_deserialize(outbuf, errbuf, sizeof(errbuf)); - if (!ret) - tvhlog(LOG_ERR, "pyepg", "htsmsg_xml_deserialize error %s", errbuf); - return ret; -} - void pyepg_init ( epggrab_module_list_t *list ) { - /* 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; + _pyepg_module.id = strdup("pyepg_sync"); + _pyepg_module.path = strdup("/usr/bin/pyepg"); + *((uint8_t*)&_pyepg_module.flags) = EPGGRAB_MODULE_SYNC + | EPGGRAB_MODULE_ASYNC + | EPGGRAB_MODULE_ADVANCED + | EPGGRAB_MODULE_SIMPLE; + _pyepg_module.enable = epggrab_module_enable; + _pyepg_module.grab = epggrab_module_grab; + _pyepg_module.parse = _pyepg_parse; + _pyepg_module.channels = &_pyepg_channels; + _pyepg_module.ch_save = _pyepg_save; + _pyepg_module.ch_add = epggrab_module_channel_add; + _pyepg_module.ch_rem = epggrab_module_channel_rem; + _pyepg_module.ch_mod = epggrab_module_channel_mod; /* Add to list */ - LIST_INSERT_HEAD(list, &_pyepg_sync, link); - LIST_INSERT_HEAD(list, &_pyepg_async, link); + LIST_INSERT_HEAD(list, &_pyepg_module, link); } diff --git a/src/epggrab/xmltv.c b/src/epggrab/xmltv.c index f96d982b..dee9f9fc 100644 --- a/src/epggrab/xmltv.c +++ b/src/epggrab/xmltv.c @@ -367,12 +367,23 @@ _xmltv_parse_tv(htsmsg_t *body, epggrab_stats_t *stats) } return save; } +#endif -/** - * - */ -static int _xmltv_parse ( htsmsg_t *data, epggrab_stats_t *stats ) +/* ************************************************************************ + * Module Setup + * ***********************************************************************/ + +epggrab_channel_tree_t _xmltv_channels; + +static void _xmltv_save ( epggrab_module_t *mod ) { + epggrab_module_channels_save(mod, "epggrab/xmltv/channels"); +} + +static int _xmltv_parse + ( epggrab_module_t *mod, htsmsg_t *data, epggrab_stats_t *stats ) +{ +#if 0 htsmsg_t *tags, *tv; if((tags = htsmsg_get_map(data, "tags")) == NULL) @@ -382,38 +393,39 @@ static int _xmltv_parse ( htsmsg_t *data, epggrab_stats_t *stats ) return 0; return _xmltv_parse_tv(tv, stats); -} #endif + return 0; +} -/* ************************************************************************ - * Config - * ***********************************************************************/ - -#if 0 -static htsmsg_t *xmltv_grabber_list ( void ) +static void _xmltv_load_grabbers ( epggrab_module_list_t *list ) { size_t i, outlen, p, n; char *outbuf; char errbuf[100]; - htsmsg_t *e = NULL, *array; + epggrab_module_t *mod; /* 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; + return; } /* 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); + mod = calloc(1, sizeof(epggrab_module_t)); + mod->id = mod->path = strdup(&outbuf[p]); + mod->name = malloc(200); + sprintf((char*)mod->name, "XMLTV: %s", &outbuf[n]); + mod->parse = _xmltv_parse; + 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; + LIST_INSERT_HEAD(list, mod, link); p = n = i + 1; } else if ( outbuf[i] == '|' ) { outbuf[i] = '\0'; @@ -422,54 +434,29 @@ static htsmsg_t *xmltv_grabber_list ( void ) i++; } free(outbuf); - - return array; } -#endif -#if TODO_XMLTV -/* ************************************************************************ - * Module Setup - * ***********************************************************************/ - -// TODO: config -// TODO: remove use of hardcoded xmltv script - -static epggrab_module_t xmltv_module; - -static const char* _xmltv_name ( void ) +void xmltv_init ( epggrab_module_list_t *list ) { - return "xmltv"; + epggrab_module_t *mod; + + /* Advanced module */ + mod = calloc(1, sizeof(epggrab_module_t)); + mod->id = strdup("xmltv"); + mod->name = strdup("XMLTV: Advanced"); + *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_ASYNC + | EPGGRAB_MODULE_SYNC + | EPGGRAB_MODULE_ADVANCED; + mod->enable = epggrab_module_enable; + mod->grab = epggrab_module_grab; + 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; + LIST_INSERT_HEAD(list, mod, link); + + /* Standard modules */ + _xmltv_load_grabbers(list); } - -static htsmsg_t* _xmltv_grab ( const char *iopts ) -{ - size_t outlen; - char *outbuf; - char errbuf[100]; - - /* Debug */ - tvhlog(LOG_DEBUG, "xmltv", "grab %s", cmd); - - /* Grab */ - outlen = spawn_and_store_stdout(cmd, NULL, &outbuf); - if ( outlen < 1 ) { - tvhlog(LOG_ERR, "xmltv", "no output detected"); - return NULL; - } - - /* Extract */ - return htsmsg_xml_deserialize(outbuf, errbuf, sizeof(errbuf)); -} - -epggrab_module_t* xmltv_init ( void ) -{ - xmltv_module.enable = NULL; - xmltv_module.disable = NULL; - xmltv_module.grab = _xmltv_grab; - xmltv_module.parse = _xmltv_parse; - xmltv_module.name = _xmltv_name; - return &xmltv_module; -} - -#endif From ec22e6a14a40a6f1fb310a2b213071dd23212337 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 6 Jun 2012 16:02:07 +0100 Subject: [PATCH 07/10] Major update to the way the epggrab configuration is handled. I now have most features avilable via the UI (except the yet to be implemented external interface config). --- src/cron.c | 2 +- src/epggrab.c | 117 ++++--- src/epggrab.h | 15 +- src/epggrab/pyepg.c | 16 +- src/epggrab/xmltv.c | 7 +- src/webui/extjs.c | 123 +++---- src/webui/static/app/epggrab.js | 572 ++++++++++++++++++++++---------- 7 files changed, 549 insertions(+), 303 deletions(-) diff --git a/src/cron.c b/src/cron.c index 6d7a9887..802cb540 100644 --- a/src/cron.c +++ b/src/cron.c @@ -113,7 +113,7 @@ int cron_set_string ( cron_t *cron, const char *str ) { int save = 0; cron_t tmp; - if (!cron->str || !strcmp(cron->str, str)) { + if (!cron->str || strcmp(cron->str, str)) { tmp.str = (char*)str; if (_cron_parse(&tmp, str)) { if (cron->str) free(cron->str); diff --git a/src/epggrab.c b/src/epggrab.c index 9cb3e609..8576f48c 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -20,7 +20,7 @@ pthread_cond_t epggrab_cond; /* Config */ uint32_t epggrab_advanced; -uint32_t epggrab_eit; +uint32_t epggrab_eitenabled; uint32_t epggrab_interval; epggrab_module_t* epggrab_module; epggrab_sched_list_t epggrab_schedule; @@ -136,10 +136,32 @@ epggrab_module_t* epggrab_module_find_by_id ( const char *id ) htsmsg_t *epggrab_module_list ( void ) { - htsmsg_t *a = htsmsg_create_list(); + 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); + else printf("no name: %s\n", m->id); + if(m->path) htsmsg_add_str(e, "path", m->path); + htsmsg_add_u32(e, "flags", m->flags); + htsmsg_add_msg(a, NULL, e); + } return a; } +void epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e ) +{ + // TODO: implement this? +} + +htsmsg_t *epggrab_module_grab + ( epggrab_module_t *mod, const char *cmd, const char *opts ) +{ + // TODO: implement this + return NULL; +} + /* ************************************************************************** * Configuration * *************************************************************************/ @@ -162,57 +184,56 @@ static int _update_str ( char **dst, const char *src ) return save; } -static int _epggrab_schedule_deserialize ( htsmsg_t *m ) +static int _epggrab_schedule_deserialize ( htsmsg_t *a ) { int save = 0; - htsmsg_t *a, *e; + htsmsg_t *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); - } + es = TAILQ_FIRST(&epggrab_schedule); + HTSMSG_FOREACH(f, a) { + if ((e = htsmsg_get_map_by_field(f))) { + if (!es) { + es = calloc(1, sizeof(epggrab_sched_t)); + save = 1; + TAILQ_INSERT_TAIL(&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; - } + 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 |= _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; } } + + /* Next */ + if (es) es = TAILQ_NEXT(es, link); } if (es) - while ( LIST_NEXT(es, link) ) { - LIST_REMOVE(es, link); + while ( (es = TAILQ_NEXT(es, link)) ) { + TAILQ_REMOVE(&epggrab_schedule, es, link); save = 1; } @@ -224,7 +245,7 @@ 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) { + TAILQ_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); @@ -246,7 +267,7 @@ static void _epggrab_load ( void ) /* Load settings */ htsmsg_get_u32(m, "advanced", &epggrab_advanced); - htsmsg_get_u32(m, "eit", &epggrab_eit); + htsmsg_get_u32(m, "eit", &epggrab_eitenabled); htsmsg_get_u32(m, "interval", &epggrab_interval); if ( (str = htsmsg_get_str(m, "module")) ) epggrab_module = epggrab_module_find_by_id(str); @@ -271,6 +292,7 @@ void epggrab_save ( void ) if ( epggrab_module ) htsmsg_add_str(m, "module", epggrab_module->id); htsmsg_add_msg(m, "schedule", _epggrab_schedule_serialize()); + htsmsg_print(m); hts_settings_save(m, "epggrab/config"); htsmsg_destroy(m); } @@ -288,9 +310,9 @@ int epggrab_set_advanced ( uint32_t advanced ) int epggrab_set_eitenabled ( uint32_t eitenabled ) { int save = 0; - if ( epggrab_eit != eitenabled ) { + if ( epggrab_eitenabled != eitenabled ) { save = 1; - eitenabled = eitenabled; + epggrab_eitenabled = eitenabled; } return save; } @@ -340,6 +362,7 @@ htsmsg_t *epggrab_get_schedule ( void ) static void _epggrab_module_run ( epggrab_module_t *mod, const char *icmd, const char *iopts ) { +#if 0 int save = 0; time_t tm1, tm2; htsmsg_t *data; @@ -399,6 +422,7 @@ static void _epggrab_module_run if (cmd) free(cmd); if (opts) free(cmd); pthread_mutex_lock(&epggrab_mutex); +#endif } /* @@ -426,7 +450,7 @@ static time_t _epggrab_thread_advanced ( void ) /* Determine which to run */ time(&ret); - LIST_FOREACH(s, &epggrab_schedule, link) { + TAILQ_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 @@ -480,10 +504,11 @@ static void* _epggrab_thread ( void* p ) void epggrab_init ( void ) { /* Defaults */ - epggrab_advanced = 0; - epggrab_eit = 1; // on air grab enabled - epggrab_interval = 12 * 3600; // hours - epggrab_module = NULL; // disabled + epggrab_advanced = 0; + epggrab_eitenabled = 1; // on air grab enabled + epggrab_interval = 12 * 3600; // hours + epggrab_module = NULL; // disabled + TAILQ_INIT(&epggrab_schedule); /* Initialise modules */ eit_init(&epggrab_modules); diff --git a/src/epggrab.h b/src/epggrab.h index 50b9887b..6b704427 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -57,10 +57,11 @@ typedef struct epggrab_module epggrab_module_t; /* * Grabber flags */ -#define EPGGRAB_MODULE_SYNC 0x1 -#define EPGGRAB_MODULE_ASYNC 0x2 -#define EPGGRAB_MODULE_SIMPLE 0x4 -#define EPGGRAB_MODULE_ADVANCED 0x8 +#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 /* * Grabber base class @@ -96,7 +97,7 @@ struct epggrab_module /* * Default module functions */ -void epggrab_module_enable ( epggrab_module_t *m, uint8_t e ); +void epggrab_module_enable_socket ( epggrab_module_t *m, uint8_t e ); htsmsg_t *epggrab_module_grab ( epggrab_module_t *m, const char *cmd, const char *opts ); void epggrab_module_channels_load ( epggrab_module_t *m ); void epggrab_module_channels_save ( epggrab_module_t *m, const char *path ); @@ -129,7 +130,7 @@ htsmsg_t* epggrab_module_list ( void ); */ typedef struct epggrab_sched { - LIST_ENTRY(epggrab_sched) link; + TAILQ_ENTRY(epggrab_sched) link; cron_t *cron; ///< Cron definition epggrab_module_t *mod; ///< Module char *cmd; ///< Command @@ -139,7 +140,7 @@ typedef struct epggrab_sched /* * Schedule list */ -LIST_HEAD(epggrab_sched_list, epggrab_sched); +TAILQ_HEAD(epggrab_sched_list, epggrab_sched); typedef struct epggrab_sched_list epggrab_sched_list_t; /* diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c index 088d3a56..29cd5b08 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -122,7 +122,7 @@ static int _pyepg_parse_brand ( htsmsg_t *data, epggrab_stats_t *stats ) } /* Set icon */ -#if TODO +#if TODO_ICON_SUPPORT if ((str = htsmsg_xml_get_cdata_str(tags, "icon"))) { save |= epg_brand_set_icon(brand, str); } @@ -166,7 +166,7 @@ static int _pyepg_parse_season ( htsmsg_t *data, epggrab_stats_t *stats ) } /* Set title */ -#if TODO +#if TODO_EXTRA_METADATA if ((str = htsmsg_xml_get_cdata_str(tags, "title"))) { save |= epg_season_set_title(season, str); } @@ -252,7 +252,7 @@ static int _pyepg_parse_episode ( htsmsg_t *data, epggrab_stats_t *stats ) /* Genre */ // TODO: can actually have multiple! -#if TODO +#if TODO_GENRE_SUPPORT if ((str = htsmsg_xml_get_cdata_str(tags, "genre"))) { // TODO: conversion? save |= epg_episode_set_genre(episode, str); @@ -396,13 +396,15 @@ static int _pyepg_parse void pyepg_init ( epggrab_module_list_t *list ) { - _pyepg_module.id = strdup("pyepg_sync"); - _pyepg_module.path = strdup("/usr/bin/pyepg"); + _pyepg_module.id = strdup("pyepg"); + _pyepg_module.path = strdup("/usr/bin/pyepg"); + _pyepg_module.name = strdup("PyEPG"); *((uint8_t*)&_pyepg_module.flags) = EPGGRAB_MODULE_SYNC | EPGGRAB_MODULE_ASYNC | EPGGRAB_MODULE_ADVANCED - | EPGGRAB_MODULE_SIMPLE; - _pyepg_module.enable = epggrab_module_enable; + | EPGGRAB_MODULE_SIMPLE + | EPGGRAB_MODULE_EXTERNAL; + _pyepg_module.enable = epggrab_module_enable_socket; _pyepg_module.grab = epggrab_module_grab; _pyepg_module.parse = _pyepg_parse; _pyepg_module.channels = &_pyepg_channels; diff --git a/src/epggrab/xmltv.c b/src/epggrab/xmltv.c index dee9f9fc..9be657fd 100644 --- a/src/epggrab/xmltv.c +++ b/src/epggrab/xmltv.c @@ -420,6 +420,8 @@ static void _xmltv_load_grabbers ( epggrab_module_list_t *list ) mod->id = mod->path = strdup(&outbuf[p]); mod->name = malloc(200); sprintf((char*)mod->name, "XMLTV: %s", &outbuf[n]); + *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_SYNC + | EPGGRAB_MODULE_SIMPLE; mod->parse = _xmltv_parse; mod->ch_add = epggrab_module_channel_add; mod->ch_rem = epggrab_module_channel_rem; @@ -446,8 +448,9 @@ void xmltv_init ( epggrab_module_list_t *list ) mod->name = strdup("XMLTV: Advanced"); *((uint8_t*)&mod->flags) = EPGGRAB_MODULE_ASYNC | EPGGRAB_MODULE_SYNC - | EPGGRAB_MODULE_ADVANCED; - mod->enable = epggrab_module_enable; + | EPGGRAB_MODULE_ADVANCED + | EPGGRAB_MODULE_EXTERNAL; + mod->enable = epggrab_module_enable_socket; mod->grab = epggrab_module_grab; mod->parse = _xmltv_parse; mod->channels = &_xmltv_channels; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 7e6c5145..fdf8ccb8 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -42,6 +42,7 @@ #include "epggrab.h" #include "epg.h" #include "iptv_input.h" +#include "epggrab/xmltv.h" static void extjs_load(htsbuf_queue_t *hq, const char *script) @@ -322,11 +323,6 @@ extjs_channels_update(htsmsg_t *in) if((s = htsmsg_get_str(c, "name")) != NULL) channel_rename(ch, s); -#if TODO_XMLTV_REMOVE_THIS - if((s = htsmsg_get_str(c, "xmltvsrc")) != NULL) - channel_set_xmltv_source(ch, xmltv_channel_find_by_displayname(s)); -#endif - if((s = htsmsg_get_str(c, "ch_icon")) != NULL) channel_set_icon(ch, s); @@ -376,11 +372,6 @@ extjs_channels(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_str(c, "name", ch->ch_name); htsmsg_add_u32(c, "chid", ch->ch_id); -#if TODO_XMLTV_REMOVE_THIS - if(ch->ch_xc != NULL) - htsmsg_add_str(c, "xmltvsrc", ch->ch_xc->xc_displayname); -#endif - if(ch->ch_icon != NULL) htsmsg_add_str(c, "ch_icon", ch->ch_icon); @@ -476,16 +467,16 @@ json_single_record(htsmsg_t *rec, const char *root) static int extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) { -#if TODO_EPGGRAB_CONFIG htsbuf_queue_t *hq = &hc->hc_reply; const char *op = http_arg_get(&hc->hc_req_args, "op"); - xmltv_channel_t *xc; - htsmsg_t *out, *array, *e, *r; - const char *s; + htsmsg_t *out, *array, *r; + const char *str; if(op == NULL) return 400; + printf("extjs_epggrab: %s\n", op); + pthread_mutex_lock(&global_lock); if(http_access_verify(hc, ACCESS_ADMIN)) { @@ -495,67 +486,64 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) pthread_mutex_unlock(&global_lock); - if(!strcmp(op, "listChannels")) { + /* Basic settings (not the advanced schedule) */ + if(!strcmp(op, "loadSettings")) { + pthread_mutex_lock(&epggrab_mutex); + r = htsmsg_create_map(); + htsmsg_add_u32(r, "eitenabled", epggrab_eitenabled); + htsmsg_add_u32(r, "advanced", epggrab_advanced); + if (epggrab_module) + htsmsg_add_str(r, "module", epggrab_module->id); + htsmsg_add_u32(r, "interval", epggrab_interval); + pthread_mutex_unlock(&epggrab_mutex); + + out = json_single_record(r, "epggrabSettings"); + + /* List of modules */ + } else if (!strcmp(op, "moduleList")) { out = htsmsg_create_map(); - array = htsmsg_create_list(); - - e = htsmsg_create_map(); - htsmsg_add_str(e, "xcTitle", "None"); - htsmsg_add_msg(array, NULL, e); - - pthread_mutex_lock(&global_lock); - LIST_FOREACH(xc, &xmltv_displaylist, xc_displayname_link) { - e = htsmsg_create_map(); - htsmsg_add_str(e, "xcTitle", xc->xc_displayname); - htsmsg_add_msg(array, NULL, e); - } - pthread_mutex_unlock(&global_lock); - + pthread_mutex_lock(&epggrab_mutex); + array = epggrab_module_list(); + pthread_mutex_unlock(&epggrab_mutex); htsmsg_add_msg(out, "entries", array); - } else if(!strcmp(op, "loadSettings")) { - - pthread_mutex_lock(&xmltv_mutex); - r = htsmsg_create_map(); - - if((s = xmltv_get_current_grabber()) != NULL) - htsmsg_add_str(r, "grabber", s); - - htsmsg_add_u32(r, "grabinterval", xmltv_grab_interval); - htsmsg_add_u32(r, "grabenable", xmltv_grab_enabled); - pthread_mutex_unlock(&xmltv_mutex); - - out = json_single_record(r, "xmltvSettings"); - - } else if(!strcmp(op, "saveSettings")) { - - pthread_mutex_lock(&xmltv_mutex); - - s = http_arg_get(&hc->hc_req_args, "grabber"); - xmltv_set_current_grabber(s); - - s = http_arg_get(&hc->hc_req_args, "grabinterval"); - xmltv_set_grab_interval(atoi(s)); - - s = http_arg_get(&hc->hc_req_args, "grabenable"); - xmltv_set_grab_enable(!!s); - - pthread_mutex_unlock(&xmltv_mutex); + /* Advanced schedule */ + } else if (!strcmp(op, "loadSchedule")) { + out = htsmsg_create_map(); + pthread_mutex_lock(&epggrab_mutex); + array = epggrab_get_schedule(); + pthread_mutex_unlock(&epggrab_mutex); + htsmsg_add_msg(out, "entries", array); + /* Save settings */ + } else if (!strcmp(op, "saveSettings") ) { + int save = 0; + pthread_mutex_lock(&epggrab_mutex); + if ( http_arg_get(&hc->hc_req_args, "advanced") ) + save |= epggrab_set_advanced(1); + else + save |= epggrab_set_advanced(0); + if ( http_arg_get(&hc->hc_req_args, "eitenabled") ) + save |= epggrab_set_eitenabled(1); + else + save |= epggrab_set_eitenabled(0); + if ( (str = http_arg_get(&hc->hc_req_args, "interval")) ) + save |= epggrab_set_interval(atoi(str)); + 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, "schedule")) ) { + if ( (array = htsmsg_json_deserialize(str)) ) { + htsmsg_print(array); + save |= epggrab_set_schedule(array); + htsmsg_destroy(array); + } + } + if (save) epggrab_save(); + pthread_mutex_unlock(&epggrab_mutex); out = htsmsg_create_map(); htsmsg_add_u32(out, "success", 1); - } else if(!strcmp(op, "listGrabbers")) { - - out = htsmsg_create_map(); - - pthread_mutex_lock(&xmltv_mutex); - array = xmltv_list_grabbers(); - pthread_mutex_unlock(&xmltv_mutex); - if(array != NULL) - htsmsg_add_msg(out, "entries", array); - } else { return HTTP_STATUS_BAD_REQUEST; } @@ -564,7 +552,6 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) htsmsg_destroy(out); http_output_content(hc, "text/x-json; charset=UTF-8"); -#endif return 0; } diff --git a/src/webui/static/app/epggrab.js b/src/webui/static/app/epggrab.js index b4baa0fc..6f122800 100644 --- a/src/webui/static/app/epggrab.js +++ b/src/webui/static/app/epggrab.js @@ -1,194 +1,422 @@ -tvheadend.grabberStore = new Ext.data.JsonStore({ - root:'entries', - fields: ['identifier','name','version','apiconfig'], - url:'xmltv', - baseParams: { - op: 'listGrabbers' - } -}); - tvheadend.epggrab = function() { + /* **************************************************************** + * Data + * ***************************************************************/ + + /* + * Module lists (I'm sure there is a better way!) + */ + var EPGGRAB_MODULE_SIMPLE = 0x04; + var EPGGRAB_MODULE_ADVANCED = 0x08; + var EPGGRAB_MODULE_EXTERNAL = 0x10; + + var moduleStore = new Ext.data.JsonStore({ + root : 'entries', + url : 'epggrab', + baseParams : { op : 'moduleList' }, + autoLoad : true, + fields : [ 'id', 'name', 'path', 'flags' ] + }); + var simpleModuleStore = new Ext.data.Store({ + recordType: moduleStore.recordType + }); + var advancedModuleStore = new Ext.data.Store({ + recordType: moduleStore.recordType + }); + var externalModuleStore = new Ext.data.Store({ + recordType: moduleStore.recordType + }); + moduleStore.on('load', function() { + moduleStore.filterBy(function(r) { + return r.get('flags') & EPGGRAB_MODULE_SIMPLE; + }); + moduleStore.each(function(r) { + simpleModuleStore.add(r.copy()); + }); + moduleStore.filterBy(function(r) { + return r.get('flags') & (EPGGRAB_MODULE_ADVANCED | EPGGRAB_MODULE_SIMPLE); + }); + moduleStore.each(function(r) { + advancedModuleStore.add(r.copy()); + }); + moduleStore.filterBy(function(r) { + return r.get('flags') & EPGGRAB_MODULE_EXTERNAL; + }); + moduleStore.each(function(r) { + externalModuleStore.add(r.copy()); + }); + }); + + /* + * Schedule + */ + + var scheduleRow = Ext.data.Record.create( + [ + { name : 'module' }, + { name : 'command' }, + { name : 'options' }, + { name : 'cron' } + ] + ); + + var scheduleStore = new Ext.data.JsonStore({ + root : 'entries', + url : 'epggrab', + baseParams : { op : 'loadSchedule' }, + autoLoad : true, + fields : [ 'module', 'command', 'options', 'cron' ] + }); + + /* + * Basic Config + */ + var confreader = new Ext.data.JsonReader( { root: 'epggrabSettings' }, - [ 'module', 'eitenable', 'advanced' ] + [ 'module', 'eitenabled', 'advanced', 'interval' ] ); - var grabberSelect = new Ext.form.ComboBox({ - loadingText: 'Loading, please wait...', - fieldLabel: 'XML-TV Source', - name: 'grabber', - width: 350, - displayField:'name', - valueField:'identifier', - store: tvheadend.grabberStore, - forceSelection: true, - editable: false, - triggerAction: 'all', - mode: 'remote', - emptyText: 'Select grabber' - }); - grabberSelect.setVisible(false); + /* **************************************************************** + * Simple Config + * ***************************************************************/ - var advancedCheck = new Ext.form.Checkbox({ - fieldLabel : 'Advanced Config', - name : 'advanced', - }); - - var moduleSelect = new Ext.form.ComboBox({ - fieldLabel : 'Grab Module', - name : 'module', - width : 150, + /* + * Module selector + */ + var simpleModule = new Ext.form.ComboBox({ + fieldLabel : 'Module', + hiddenName : 'module', + width : 300, + valueField : 'id', + displayField : 'name', + forceSelection : true, + editable : false, + mode : 'local', + triggerAction : 'all', + store : simpleModuleStore + }); + + /* + * Interval selector + */ + var intervalUnits = [ + [ 86400, 'Days' ], + [ 3600, 'Hours' ], + [ 60, 'Minutes' ], + [ 1, 'Seconds' ] + ]; + var intervalValue = new Ext.form.NumberField({ + width : 300, + allowNegative : false, + allowDecimals : false, + minValue : 1, + maxValue : 7, + value : 1, + fieldLabel : 'Grab interval', + name : 'intervalValue', + listeners : { + 'valid' : function (e) { + v = e.getValue() * intervalUnit.getValue(); + interval.setValue(v); + } + } + }) + var intervalUnit = new Ext.form.ComboBox({ + name : 'intervalUnit', + width : 300, valueField : 'key', displayField : 'value', + value : 86400, forceSelection : true, editable : false, triggerAction : 'all', mode : 'local', store : new Ext.data.SimpleStore({ - fields : [ 'key', 'value' ], - data : [ - [ 'xmltv', 'XMLTV' ], - [ 'pyepg', 'PyEPG' ], - ] - }) + fields : [ 'key', 'value' ], + data : intervalUnits + }), + listeners : { + 'change' : function (e, n, o) { + intervalValue.maxValue = (7 * 86400) / n; + intervalValue.validate(); + } + } + }); + var interval = new Ext.form.Hidden({ + name : 'interval', + value : 86400, + listeners : { + 'enable' : function (e) { + v = e.getValue(); + for ( i = 0; i < intervalUnits.length; i++ ) { + u = intervalUnits[i][0]; + if ( (v % u) == 0 ) { + intervalUnit.setValue(u); + intervalValue.maxValue = (7 * 86400) / u; + intervalValue.setValue(v / u); + intervalValue.validate(); + break; + } + } + } + } }); - var confpanel = new Ext.FormPanel( - { - title : 'EPG Grabber', - iconCls : 'xml', - border : false, - bodyStyle : 'padding:15px', - labelAlign : 'right', - labelWidth : 200, - waitMsgTarget : true, - reader : confreader, - layout : 'form', - defaultType : 'textfield', - items : [ - moduleSelect, - new Ext.form.Checkbox({ - fieldLabel : 'Enable EIT', - name : 'eitenable' - }), - advancedCheck, - grabberSelect - ], - tbar: [ - { - tooltip: 'Save changes made to configuration below', - iconCls:'save', - text: "Save configuration", - handler: saveChanges - }, - '->', - { - text: 'Help', - handler: function() { - new tvheadend.help('XMLTV configuration', 'config_xmltv.html'); - } - } - ] - } - ); + var simplePanel = new Ext.form.FieldSet({ + title : 'Simple Config', + height : 120, + width : 900, + items : [ + simpleModule, + intervalValue, + intervalUnit + ] + }); - /* - * Event handlers + /* **************************************************************** + * Advanced Config + * ***************************************************************/ + + /* + * Schedule */ - advancedCheck.on('enable', function(e) { - Ext.MessageBox.alert('Test', 'testing'); - }); - - moduleSelect.on('select', function(c,r,i) { - alert('select module'); - }); - - confpanel.on('render', function() { - confpanel.getForm().load({ - url:'xmltv', - params:{'op':'loadSettings'}, - success:function(form, action) { - confpanel.enable(); - } - }); - }); - - - grabberSelect.on('select', function(c,r,i) { - - Ext.MessageBox.alert('XMLTV', - 'Make sure that the grabber is properly ' + - 'configured before saving configuration.
'+ - '
' + - 'To configure manually execute the ' + - 'following command in a shell on the ' + - 'server:
' + - '$ ' + r.data.identifier + - ' --configure
' + - '
' + - 'Note: It is important to configure the ' + - 'grabber using the same userid as tvheadend '+ - 'since most grabbers save their '+ - 'configuration in the users home directory.'+ - '
' + - '
' + - 'Grabber version: ' + r.data.version - ); - -/* - if(r.data.apiconfig) { - - Ext.MessageBox.confirm('XMLTV', - 'Configure grabber? ' + - 'If you know that the grabber is already '+ - 'set up or if you want to configure it '+ - 'manually you may skip this step', - function(button) { - Ext.MessageBox.alert('XMLTV', - 'oops, embedded '+ - 'config not '+ - 'implemeted yet'); - } - ); - - } else { - Ext.MessageBox.alert('XMLTV', - 'This grabber does not support being ' + - 'configured from external application ' + - '(such as Tvheadend).
' + - 'Make sure that the grabber is properly ' + - 'configured before saving configuration.
'+ - '
' + - 'To configure manually execute the ' + - 'following command in a shell on the ' + - 'server:
' + - '$ ' + r.data.identifier + - ' --configure
' + - '
' + - 'Note: It is important to configure the ' + - 'grabber using the same userid as tvheadend '+ - 'since most grabbers save their '+ - 'configuration in the users home directory.'+ - '
' + - '
' + - 'Grabber version: ' + r.data.version - ); - } -*/ - }); - - function saveChanges() { - confpanel.getForm().submit({ - url:'xmltv', - params:{'op':'saveSettings'}, - waitMsg:'Saving Data...', - failure: function(form, action) { - Ext.Msg.alert('Save failed', action.result.errormsg); - } - }); + var scheduleColumnModel = new Ext.grid.ColumnModel([ + { + header : 'Module', + dataIndex : 'module', + width : 300, + sortable : false, + renderer : function (v) + { + if ( v != "" ) { + i = advancedModuleStore.find('id', v); + v = advancedModuleStore.getAt(i).get('name'); + } + return v; + }, + editor : new Ext.form.ComboBox({ + valueField : 'id', + displayField : 'name', + editable : false, + mode : 'local', + triggerAction : 'all', + store : advancedModuleStore + }), + }, + { + header : 'Cron', + dataIndex : 'cron', + width : 150, + sortable : false, + editor : new Ext.form.TextField() + }, + { + header : 'Command', + dataIndex : 'command', + width : 200, + sortable : false, + editor : new Ext.form.TextField() + }, + { + dataIndex : 'options', + header : 'Options', + width : 200, + sortable : false, + editor : new Ext.form.TextField() } + ]); + scheduleColumnModel.isCellEditable = function (ci, ri) + { + if (ci == 0) return true; + m = scheduleStore.getAt(ri).get('module'); + if (m == "") return false; + m = advancedModuleStore.find('id', m); + m = advancedModuleStore.getAt(m); + if (!(m.get('flags') & EPGGRAB_MODULE_ADVANCED)) { + return (ci == 1); + } + return true; + }; - return confpanel; + scheduleSelectModel = new Ext.grid.RowSelectionModel({ + singleSelect : false, + }); + scheduleSelectModel.on('selectionchange', function(s) { + delButton.setDisabled(s.getCount() == 0); + }); + + var addButton = new Ext.Button({ + text : 'Add', + iconCls : 'add', + handler : function () { + scheduleStore.add(new scheduleRow({ + module : '', + cron : '', + command : '', + options : ''} + )); + } + }); + + var delButton = new Ext.Button({ + text : 'Delete', + iconCls : 'remove', + disabled : true, + handler : function () { + var s = schedulePanel.getSelectionModel().each(function(r){ + scheduleStore.remove(r); + }); + } + }); + + var schedulePanel = new Ext.grid.EditorGridPanel({ + store : scheduleStore, + cm : scheduleColumnModel, + sm : scheduleSelectModel, + width : 850, + height : 150, + frame : true, + viewConfig : { + forceFit : true, + markDirty : false + }, + iconCls : 'icon-grid', + tbar : [ + addButton, + delButton + ], + listeners : { + 'afteredit' : function (r) { + if ( r.field == 'module' ) { + d = scheduleStore.getAt(r.row); + c = ''; + if ( r.value != "" ) { + i = advancedModuleStore.find('id', r.value); + m = advancedModuleStore.getAt(i); + c = m.get('path'); + } + d.set('command', c) + } + }, + 'select' : function(r) { + delButton.setDisabled(false); + } + } + }); + + var advancedPanel = new Ext.form.FieldSet({ + title : 'Advanced Config', + height : 200, + width : 900, + items : [ + // TODO: external editors + schedulePanel + ] + }); + + /* **************************************************************** + * Form + * ***************************************************************/ + + var advancedCheck = new Ext.form.Checkbox({ + fieldLabel : 'Advanced Config', + name : 'advanced', + listeners : { + 'check' : function (e, v) { + simplePanel.setVisible(!v); + advancedPanel.setVisible(v); + } + } + }); + + var eitCheck = new Ext.form.Checkbox({ + fieldLabel : 'EIT Enabled', + name : 'eitenabled' + }); + + var saveButton = new Ext.Button({ + text : "Save configuration", + tooltip : 'Save changes made to configuration below', + iconCls :'save', + handler : saveChanges, + }); + + var helpButton = new Ext.Button({ + text : 'Help', + handler : function() { + alert('TODO: help info'); + } + }); + + var confpanel = new Ext.FormPanel({ + title : 'EPG Grabber', + iconCls : 'xml', + border : false, + bodyStyle : 'padding:15px', + labelAlign : 'left', + labelWidth : 150, + waitMsgTarget : true, + reader : confreader, + layout : 'form', + defaultType : 'textfield', + items : [ + interval, + advancedCheck, + eitCheck, + simplePanel, + advancedPanel + ], + tbar: [ + saveButton, + '->', + helpButton + ] + }); + + // TODO: HACK: bug in extjs seems to cause sub-components of the form not to render! + confpanel.on('afterlayout', function() + { + simplePanel.syncSize(); + advancedPanel.syncSize(); + simplePanel.setVisible(!advancedCheck.getValue()); + advancedPanel.setVisible(advancedCheck.getValue()); + }); + + /* **************************************************************** + * Load/Save + * ***************************************************************/ + + confpanel.on('render', function() { + confpanel.getForm().load({ + url : 'epggrab', + params : { op : 'loadSettings' }, + success : function ( form, action ) { + confpanel.enable(); + } + }); + }); + + function saveChanges() { + data = []; + scheduleStore.each(function(r) { + data.push(r.data); + }); + json = Ext.util.JSON.encode(data); + confpanel.getForm().submit({ + url : 'epggrab', + params : { op : 'saveSettings', schedule : json }, + waitMsg : 'Saving Data...', + success : function(form, action) { + scheduleStore.commitChanges(); + }, + failure : function (form, action) { + Ext.Msg.alert('Save failed', action.result.errormsg); + } + }); + } + + return confpanel; } From 5eae7e8aefa20c1f6ce89beed153f171b6aa3deb Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 6 Jun 2012 16:07:15 +0100 Subject: [PATCH 08/10] Some minor bugfixes and remove debug code. --- src/epggrab.c | 9 ++++----- src/webui/extjs.c | 3 --- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/epggrab.c b/src/epggrab.c index 8576f48c..e21eba12 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -142,7 +142,6 @@ htsmsg_t *epggrab_module_list ( void ) e = htsmsg_create_map(); htsmsg_add_str(e, "id", m->id); if(m->name) htsmsg_add_str(e, "name", m->name); - else printf("no name: %s\n", m->id); if(m->path) htsmsg_add_str(e, "path", m->path); htsmsg_add_u32(e, "flags", m->flags); htsmsg_add_msg(a, NULL, e); @@ -231,11 +230,12 @@ static int _epggrab_schedule_deserialize ( htsmsg_t *a ) if (es) es = TAILQ_NEXT(es, link); } - if (es) - while ( (es = TAILQ_NEXT(es, link)) ) { + if (es) { + do { TAILQ_REMOVE(&epggrab_schedule, es, link); save = 1; - } + } while ( (es = TAILQ_NEXT(es, link)) ); + } return save; } @@ -292,7 +292,6 @@ void epggrab_save ( void ) if ( epggrab_module ) htsmsg_add_str(m, "module", epggrab_module->id); htsmsg_add_msg(m, "schedule", _epggrab_schedule_serialize()); - htsmsg_print(m); hts_settings_save(m, "epggrab/config"); htsmsg_destroy(m); } diff --git a/src/webui/extjs.c b/src/webui/extjs.c index fdf8ccb8..49d64430 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -475,8 +475,6 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) if(op == NULL) return 400; - printf("extjs_epggrab: %s\n", op); - pthread_mutex_lock(&global_lock); if(http_access_verify(hc, ACCESS_ADMIN)) { @@ -534,7 +532,6 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) save |= epggrab_set_module_by_id(str); if ( (str = http_arg_get(&hc->hc_req_args, "schedule")) ) { if ( (array = htsmsg_json_deserialize(str)) ) { - htsmsg_print(array); save |= epggrab_set_schedule(array); htsmsg_destroy(array); } From 2c8010667cd536b3a3144810d1d1a5363518080d Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Wed, 6 Jun 2012 17:08:15 +0100 Subject: [PATCH 09/10] Fixed some bugs in cron code and re-enabled basic grabber code (still not fully functional). --- src/cron.c | 16 ++++++++--- src/cron.h | 1 + src/epggrab.c | 72 +++++++++++++++++++++++++++++++++++++++-------- src/epggrab/eit.c | 12 ++------ 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/cron.c b/src/cron.c index 802cb540..74e4ee9f 100644 --- a/src/cron.c +++ b/src/cron.c @@ -19,12 +19,12 @@ static int _cron_parse_field { int i = 0, j = 0, sn = -1, en = -1, mn = -1; *field = 0; - while ( str[i] != '\0' ) { + while ( 1 ) { if ( str[i] == '*' ) { sn = 0; en = 64; j = -1; - } else if ( str[i] == ',' || str[i] == ' ' ) { + } else if ( str[i] == ',' || str[i] == ' ' || str[i] == '\0' ) { if (j >= 0) sscanf(str+j, "%d", en == -1 ? (sn == -1 ? &sn : &en) : &mn); if (en == -1) en = sn; @@ -34,6 +34,7 @@ static int _cron_parse_field *field |= (0x1LL << (sn - offset)); sn++; } + if ( str[i] == '\0' ) break; j = i+1; sn = en = mn = -1; if (str[i] == ' ') break; @@ -145,13 +146,20 @@ int cron_is_time ( cron_t *cron ) return ret; } +// TODO: use this as part of cron_next/cron_is_time +void cron_run ( cron_t *cron ) +{ +} + // TODO: do proper search for next time time_t cron_next ( cron_t *cron ) { time_t now; time(&now); - now += 60; - return (now / 60) * 60; // round to start of minute + now += 62; // TODO: hack because timer goes off just before second tick + now /= 60; + now *= 60; + return now; } diff --git a/src/cron.h b/src/cron.h index 6c42b11d..b42904c9 100644 --- a/src/cron.h +++ b/src/cron.h @@ -19,6 +19,7 @@ const char *cron_get_string ( cron_t *cron ); int cron_set_string ( cron_t *cron, const char *str ); int cron_is_time ( cron_t *cron ); time_t cron_next ( cron_t *cron ); +void cron_run ( cron_t *cron ); void cron_serialize ( cron_t *cron, htsmsg_t *msg ); cron_t *cron_deserialize ( htsmsg_t *msg ); diff --git a/src/epggrab.c b/src/epggrab.c index e21eba12..59d81673 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "htsmsg.h" #include "settings.h" #include "tvheadend.h" @@ -12,6 +13,8 @@ #include "epggrab/xmltv.h" #include "epggrab/pyepg.h" #include "channels.h" +#include "spawn.h" +#include "htsmsg_xml.h" /* Thread protection */ int epggrab_confver; @@ -156,9 +159,50 @@ void epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e ) htsmsg_t *epggrab_module_grab ( epggrab_module_t *mod, const char *cmd, const char *opts ) -{ - // TODO: implement this - return NULL; +{ + int i, outlen; + char *outbuf; + char errbuf[100]; + const char **argv = NULL; + wordexp_t we; + htsmsg_t *ret; + + /* Determine command */ + if ( !cmd || cmd[0] == '\0' ) + cmd = mod->path; + if ( !cmd ) { + tvhlog(LOG_ERR, "epggrab", "invalid configuraton no command specified"); + return NULL; + } + printf("cmd = %s, opts = %s\n", cmd, opts); + + /* Parse opts */ + if (opts) { + wordexp(opts, &we, 0); + argv = calloc(we.we_wordc+2, sizeof(char*)); + argv[0] = cmd; + for ( i = 0; i < we.we_wordc; i++ ) { + argv[i + 1] = we.we_wordv[i]; + } + } + + /* Debug */ + tvhlog(LOG_DEBUG, "pyepg", "grab %s %s", cmd, opts ?: ""); + + /* Grab */ + outlen = spawn_and_store_stdout(cmd, (char*const*)argv, &outbuf); + free(argv); + wordfree(&we); + if ( outlen < 1 ) { + tvhlog(LOG_ERR, "pyepg", "no output detected"); + return NULL; + } + + /* Extract */ + ret = htsmsg_xml_deserialize(outbuf, errbuf, sizeof(errbuf)); + if (!ret) + tvhlog(LOG_ERR, "pyepg", "htsmsg_xml_deserialize error %s", errbuf); + return ret; } /* ************************************************************************** @@ -361,7 +405,6 @@ htsmsg_t *epggrab_get_schedule ( void ) static void _epggrab_module_run ( epggrab_module_t *mod, const char *icmd, const char *iopts ) { -#if 0 int save = 0; time_t tm1, tm2; htsmsg_t *data; @@ -372,8 +415,8 @@ static void _epggrab_module_run if ( !mod || !mod->grab || !mod->parse ) return; /* Dup before unlock */ - if ( !icmd ) cmd = strdup(icmd); - if ( !iopts ) opts = strdup(iopts); + if ( icmd ) cmd = strdup(icmd); + if ( iopts ) opts = strdup(iopts); pthread_mutex_unlock(&epggrab_mutex); /* Grab */ @@ -419,9 +462,8 @@ static void _epggrab_module_run /* Free a re-lock */ if (cmd) free(cmd); - if (opts) free(cmd); + if (opts) free(opts); pthread_mutex_lock(&epggrab_mutex); -#endif } /* @@ -444,19 +486,25 @@ static time_t _epggrab_thread_simple ( void ) */ static time_t _epggrab_thread_advanced ( void ) { - time_t ret; + time_t ret, now; epggrab_sched_t *s; /* Determine which to run */ - time(&ret); + time(&now); + ret = now + 3600; // default TAILQ_FOREACH(s, &epggrab_schedule, link) { if ( cron_is_time(s->cron) ) { + cron_run(s->cron); _epggrab_module_run(s->mod, s->cmd, s->opts); - break; // TODO: can only run once else config may have changed + return now + 10; + // TODO: don't try to interate the list, it'll break due to locking + // module (i.e. _epggrab_module_run() unlocks) + } else { + ret = MIN(ret, cron_next(s->cron)); } } - return ret + 60; + return ret;//now + 30; } /* diff --git a/src/epggrab/eit.c b/src/epggrab/eit.c index faf811ea..106699d3 100644 --- a/src/epggrab/eit.c +++ b/src/epggrab/eit.c @@ -49,9 +49,7 @@ void eit_callback ( channel_t *ch, int id, time_t start, time_t stop, if (!ch || !ch->ch_name || !ch->ch_name[0]) return; /* Disabled? */ -#if TODO_REENABLE_THIS - if (epggrab_eit_disabled) return; -#endif + if (!epggrab_eitenabled) return; /* Find broadcast */ ebc = epg_broadcast_find_by_time(ch, start, stop, 1, &save); @@ -89,18 +87,12 @@ void eit_callback ( channel_t *ch, int id, time_t start, time_t stop, * ***********************************************************************/ static epggrab_module_t _eit_mod; -static uint8_t _eit_enabled; - -static void _eit_enable ( epggrab_module_t *mod, uint8_t e ) -{ - _eit_enabled = e; -} 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; - _eit_mod.enable = _eit_enable; LIST_INSERT_HEAD(list, &_eit_mod, link); + // Note: this is mostly ignored anyway as EIT is treated as a special case! } From 704028d6259c9f420815e890d0a20bdf75ef4685 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 7 Jun 2012 09:41:15 +0100 Subject: [PATCH 10/10] Committing this for posterity, but going to do a massive u-turn on the whole epggrab setup. --- src/cron.c | 34 ++++++++++++---------------------- src/cron.h | 4 +--- src/epggrab.c | 13 ++++++------- 3 files changed, 19 insertions(+), 32 deletions(-) diff --git a/src/cron.c b/src/cron.c index 74e4ee9f..33985976 100644 --- a/src/cron.c +++ b/src/cron.c @@ -7,6 +7,7 @@ typedef struct cron { char *str; ///< String representation + time_t last; ///< Last execution time uint64_t min; ///< Minutes uint32_t hour; ///< Hours uint32_t dom; ///< Day of month @@ -126,16 +127,22 @@ int cron_set_string ( cron_t *cron, const char *str ) return save; } -int cron_is_time ( cron_t *cron ) +time_t cron_run ( cron_t *cron ) { + time_t t; + struct tm now; int ret = 1; uint64_t b = 0x1; /* Get the current time */ - time_t t = time(NULL); - struct tm now; - localtime_r(&t, &now); - + time(&t); + localtime_t(&t, &now); + + /* Find next event */ + if ( now->min == cron->last->min) { + + } + /* Check */ if ( ret && !((b << now.tm_min) & cron->min) ) ret = 0; if ( ret && !((b << now.tm_hour) & cron->hour) ) ret = 0; @@ -146,23 +153,6 @@ int cron_is_time ( cron_t *cron ) return ret; } -// TODO: use this as part of cron_next/cron_is_time -void cron_run ( cron_t *cron ) -{ -} - -// TODO: do proper search for next time -time_t cron_next ( cron_t *cron ) -{ - time_t now; - time(&now); - now += 62; // TODO: hack because timer goes off just before second tick - now /= 60; - now *= 60; - return now; -} - - void cron_serialize ( cron_t *cron, htsmsg_t *msg ) { htsmsg_add_str(msg, "cron", cron->str); diff --git a/src/cron.h b/src/cron.h index b42904c9..025f664b 100644 --- a/src/cron.h +++ b/src/cron.h @@ -17,9 +17,7 @@ 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 ); -int cron_is_time ( cron_t *cron ); -time_t cron_next ( cron_t *cron ); -void cron_run ( cron_t *cron ); +int cron_run ( cron_t *cron, time_t *next ); void cron_serialize ( cron_t *cron, htsmsg_t *msg ); cron_t *cron_deserialize ( htsmsg_t *msg ); diff --git a/src/epggrab.c b/src/epggrab.c index 59d81673..6e8d03ec 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -486,25 +486,24 @@ static time_t _epggrab_thread_simple ( void ) */ static time_t _epggrab_thread_advanced ( void ) { - time_t ret, now; + time_t now, ret, tmp; epggrab_sched_t *s; /* Determine which to run */ time(&now); - ret = now + 3600; // default + ret = now + 3600; // once an hour if no entries TAILQ_FOREACH(s, &epggrab_schedule, link) { - if ( cron_is_time(s->cron) ) { - cron_run(s->cron); + if ( cron_run(s->cron, &tmp) ) { + ret = now; // re-run immediately _epggrab_module_run(s->mod, s->cmd, s->opts); - return now + 10; // TODO: don't try to interate the list, it'll break due to locking // module (i.e. _epggrab_module_run() unlocks) } else { - ret = MIN(ret, cron_next(s->cron)); + ret = MIN(ret, tmp); } } - return ret;//now + 30; + return ret; } /*