diff --git a/src/epg.c b/src/epg.c index a265313e..b4553f4c 100644 --- a/src/epg.c +++ b/src/epg.c @@ -16,16 +16,6 @@ * along with this program. If not, see . */ -/* - * TODO: list: - * - * - sanity checks on _destroy calls could just do what we're told and - * unreference all links? - * - * - does the broadcast <-> channel referencing need to be 2 way? - * i.e. do we need to hold onto the CHANNEL info for DVR held broadcasts? - */ - #include #include #include @@ -54,13 +44,6 @@ epg_object_list_t epg_objects[EPG_HASH_WIDTH]; epg_object_tree_t epg_brands; epg_object_tree_t epg_seasons; epg_object_tree_t epg_episodes; -epg_object_tree_t epg_channels; - -/* Unmapped */ -LIST_HEAD(epg_channel_unmapped_list, epg_channel); -LIST_HEAD(channel_unmapped_list, channel); -struct epg_channel_unmapped_list epg_channel_unmapped; -struct channel_unmapped_list channel_unmapped; /* Unreferenced */ epg_object_list_t epg_object_unref; @@ -89,13 +72,6 @@ static int _ebc_win_cmp ( const void *a, const void *b ) return 0; } -static int _epg_channel_cmp ( epg_channel_t *ec, channel_t *ch ) -{ - int ret = 0; - if ( ec->name && !strcmp(ec->name, ch->ch_name) ) ret = 1; - return ret; -} - /* ************************************************************************** * Setup / Update * *************************************************************************/ @@ -113,6 +89,8 @@ static int _epg_write ( int fd, htsmsg_t *m ) free(msgdata); if(w == msglen) ret = 0; } + } else { + ret = 0; } if(ret) { tvhlog(LOG_DEBUG, "epg", "failed to store epg to disk"); @@ -132,18 +110,13 @@ static int _epg_write_sect ( int fd, const char *sect ) void epg_save ( void ) { int fd; - epg_object_t *eo, *ec; + epg_object_t *eo; + channel_t *ch; epggrab_stats_t stats; fd = hts_settings_open_file(1, "epgdb"); - /* Channels */ memset(&stats, 0, sizeof(stats)); - if ( _epg_write_sect(fd, "channels") ) return; - RB_FOREACH(eo, &epg_channels, glink) { - if (_epg_write(fd, epg_channel_serialize((epg_channel_t*)eo))) return; - stats.channels.total++; - } if ( _epg_write_sect(fd, "brands") ) return; RB_FOREACH(eo, &epg_brands, glink) { if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) return; @@ -160,8 +133,8 @@ void epg_save ( void ) stats.episodes.total++; } if ( _epg_write_sect(fd, "broadcasts") ) return; - RB_FOREACH(ec, &epg_channels, glink) { - RB_FOREACH(eo, &((epg_channel_t*)ec)->schedule, glink) { + RB_FOREACH(ch, &channel_name_tree, ch_name_link) { + RB_FOREACH(eo, &ch->ch_epg_schedule, glink) { if (_epg_write(fd, epg_broadcast_serialize((epg_broadcast_t*)eo))) return; stats.broadcasts.total++; } @@ -169,7 +142,6 @@ void epg_save ( void ) /* Stats */ tvhlog(LOG_DEBUG, "epg", "database saved"); - tvhlog(LOG_DEBUG, "epg", " channels %d", stats.channels.total); tvhlog(LOG_DEBUG, "epg", " brands %d", stats.brands.total); tvhlog(LOG_DEBUG, "epg", " seasons %d", stats.seasons.total); tvhlog(LOG_DEBUG, "epg", " episodes %d", stats.episodes.total); @@ -230,10 +202,6 @@ void epg_init ( void ) if (sect) free(sect); sect = strdup(s); - /* Channel */ - } else if ( !strcmp(sect, "channels") ) { - if (epg_channel_deserialize(m, 1, &save)) stats.channels.total++; - /* Brand */ } else if ( !strcmp(sect, "brands") ) { if (epg_brand_deserialize(m, 1, &save)) stats.brands.total++; @@ -297,8 +265,10 @@ void epg_updated ( void ) static void _epg_object_destroy ( epg_object_t *eo, epg_object_tree_t *tree ) { + assert(eo->refcount == 0); if (eo->uri) free(eo->uri); if (tree) RB_REMOVE(tree, eo, glink); + LIST_REMOVE(eo, hlink); } static void _epg_object_getref ( epg_object_t *eo ) @@ -335,7 +305,7 @@ static epg_object_t *_epg_object_find if (!eo->putref) eo->putref = _epg_object_putref; _epg_object_idx++; LIST_INSERT_HEAD(&epg_object_unref, eo, ulink); - LIST_INSERT_HEAD(&epg_objects[eo->id & EPG_HASH_WIDTH], eo, hlink); + LIST_INSERT_HEAD(&epg_objects[eo->id & EPG_HASH_MASK], eo, hlink); } } @@ -366,7 +336,7 @@ static epg_object_t *_epg_object_find_by_uri static epg_object_t *_epg_object_find_by_id2 ( uint64_t id ) { epg_object_t *eo; - LIST_FOREACH(eo, &epg_objects[id & EPG_HASH_WIDTH], hlink) { + LIST_FOREACH(eo, &epg_objects[id & EPG_HASH_MASK], hlink) { if (eo->id == id) return eo; } return NULL; @@ -410,6 +380,7 @@ epg_brand_t* epg_brand_find_by_uri static epg_object_t *skel = NULL; if ( !skel ) { skel = calloc(1, sizeof(epg_brand_t)); + skel->type = EPG_BRAND; skel->destroy = _epg_brand_destroy; } return (epg_brand_t*) @@ -543,6 +514,7 @@ epg_season_t* epg_season_find_by_uri static epg_object_t *skel = NULL; if ( !skel ) { skel = calloc(1, sizeof(epg_season_t)); + skel->type = EPG_SEASON; skel->destroy = _epg_season_destroy; } return (epg_season_t*) @@ -694,6 +666,7 @@ epg_episode_t* epg_episode_find_by_uri static epg_object_t *skel = NULL; if ( !skel ) { skel = calloc(1, sizeof(epg_episode_t)); + skel->type = EPG_EPISODE; skel->destroy = _epg_episode_destroy; } return (epg_episode_t*) @@ -939,17 +912,17 @@ static void _epg_broadcast_destroy ( epg_object_t *eo ) } epg_broadcast_t* epg_broadcast_find_by_time - ( epg_channel_t *channel, time_t start, time_t stop, int create, int *save ) + ( channel_t *channel, time_t start, time_t stop, int create, int *save ) { return epg_channel_get_broadcast(channel, start, stop, create, save); } // TODO: allow optional channel parameter? -epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id, epg_channel_t *ec ) +epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id, channel_t *ch ) { epg_object_t *eo = NULL; - if ( ec ) { - eo = _epg_object_find_by_id(id, &((epg_channel_t*)ec)->schedule); + if ( ch ) { + eo = _epg_object_find_by_id(id, &ch->ch_epg_schedule); } else { eo = _epg_object_find_by_id2(id); } @@ -984,31 +957,27 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast ) { htsmsg_t *m; if (!broadcast) return NULL; - if (!broadcast->channel || !broadcast->channel->_.uri) return NULL; if (!broadcast->episode || !broadcast->episode->_.uri) return NULL; m = htsmsg_create_map(); - htsmsg_add_u32(m, "id", broadcast->_.id); + htsmsg_add_u64(m, "id", broadcast->_.id); htsmsg_add_u32(m, "start", broadcast->start); htsmsg_add_u32(m, "stop", broadcast->stop); if (broadcast->channel) - htsmsg_add_str(m, "channel", broadcast->channel->_.uri); + htsmsg_add_u32(m, "channel", broadcast->channel->ch_id); htsmsg_add_str(m, "episode", broadcast->episode->_.uri); - if (broadcast->dvb_id) - htsmsg_add_u32(m, "dvb-id", broadcast->dvb_id); - // TODO: add other metadata fields return m; } epg_broadcast_t *epg_broadcast_deserialize ( htsmsg_t *m, int create, int *save ) { + channel_t *ch; epg_broadcast_t *ebc; - epg_channel_t *ec; epg_episode_t *ee; const char *str; - uint32_t start, stop; + uint32_t chid, start, stop; uint64_t id; // TODO: need to handle broadcasts without a channel @@ -1018,12 +987,12 @@ epg_broadcast_t *epg_broadcast_deserialize if ( htsmsg_get_u64(m, "id", &id) ) return NULL; if ( htsmsg_get_u32(m, "start", &start) ) return NULL; if ( htsmsg_get_u32(m, "stop", &stop) ) return NULL; - if ( !(str = htsmsg_get_str(m, "channel")) ) return NULL; - if ( !(ec = epg_channel_find_by_uri(str, 0, NULL)) ) return NULL; + if ( htsmsg_get_u32(m, "channel", &chid) ) return NULL; + if ( !(ch = channel_find_by_identifier(chid)) ) return NULL; if ( !(str = htsmsg_get_str(m, "episode")) ) return NULL; if ( !(ee = epg_episode_find_by_uri(str, 0, NULL)) ) return NULL; - ebc = epg_broadcast_find_by_time(ec, start, stop, create, save); + ebc = epg_broadcast_find_by_time(ch, start, stop, create, save); if ( !ebc ) return NULL; *save |= epg_broadcast_set_episode(ebc, ee); @@ -1032,11 +1001,7 @@ epg_broadcast_t *epg_broadcast_deserialize ebc->_.id = id; if ( id >= _epg_object_idx ) _epg_object_idx = id + 1; -#if TODO_BROADCAST_METADATA - if ( !htsmsg_get_u32(m, "dvb-id", &u32) ) - save |= epg_broadcast_set_dvb_id(ebc, u32); // TODO: more metadata -#endif return ebc; } @@ -1050,281 +1015,101 @@ static void _epg_channel_timer_callback ( void *p ) time_t next = 0; epg_object_t *eo; epg_broadcast_t *ebc, *cur; - epg_channel_t *ec = (epg_channel_t*)p; + channel_t *ch = (channel_t*)p; /* Clear now/next */ - cur = ec->now; - ec->now = ec->next = NULL; + cur = ch->ch_epg_now; + ch->ch_epg_now = ch->ch_epg_next = NULL; /* Check events */ - while ( (eo = RB_FIRST(&ec->schedule)) ) { + while ( (eo = RB_FIRST(&ch->ch_epg_schedule)) ) { ebc = (epg_broadcast_t*)eo; /* Expire */ if ( ebc->stop <= dispatch_clock ) { - RB_REMOVE(&ec->schedule, eo, glink); + RB_REMOVE(&ch->ch_epg_schedule, eo, glink); tvhlog(LOG_DEBUG, "epg", "expire event %lu from %s", - eo->id, ec->_.uri); + eo->id, ch->ch_name); eo->putref(eo); continue; // skip to next /* No now */ } else if ( ebc->start > dispatch_clock ) { - ec->next = ebc; - next = ebc->start; + ch->ch_epg_next = ebc; + next = ebc->start; /* Now/Next */ } else { - ec->now = ebc; - ec->next = (epg_broadcast_t*)RB_NEXT(eo, glink); - next = ebc->stop; + ch->ch_epg_now = ebc; + ch->ch_epg_next = (epg_broadcast_t*)RB_NEXT(eo, glink); + next = ebc->stop; } break; } tvhlog(LOG_DEBUG, "epg", "now/next %lu/%lu set on %s", - ec->now ? ec->now->_.id : 0, - ec->next ? ec->next->_.id : 0, - ec->_.uri); + ch->ch_epg_now ?: 0, + ch->ch_epg_next ?: 0, + ch->ch_name); /* re-arm */ if ( next ) { tvhlog(LOG_DEBUG, "epg", "arm channel timer @ %lu for %s", - next, ec->_.uri); - gtimer_arm_abs(&ec->expire, _epg_channel_timer_callback, ec, next); + next, ch->ch_name); + gtimer_arm_abs(&ch->ch_epg_timer, _epg_channel_timer_callback, ch, next); } /* Update HTSP */ - if ( (cur != ec->now) && ec->channel ) { + if ( cur != ch->ch_epg_now ) { tvhlog(LOG_DEBUG, "epg", "inform HTSP of now event change on %s", - ec->_.uri); - htsp_channel_update_current(ec->channel); + ch->ch_name); + htsp_channel_update_current(ch); } } -static void _epg_channel_destroy ( epg_object_t *eo ) +void epg_channel_unlink ( channel_t *ch ) { - epg_object_t *ebc; - epg_channel_t *ec = (epg_channel_t*)eo; - if (ec->channel) { - tvhlog(LOG_CRIT, "epg", "attempt to destroy mapped channel"); - assert(0); + epg_object_t *eo; + while ( (eo = RB_FIRST(&ch->ch_epg_schedule)) ) { + RB_REMOVE(&ch->ch_epg_schedule, eo, glink); } -#if TODO_WHAT_SHOULD_BE_DONE - if (RB_FIRST(&ec->schedule)) { - tvhlog(LOG_CRIT, "epg", "attempt to destroy channel with schedule"); - assert(0); - } -#endif - _epg_object_destroy(eo, &epg_channels); - // TODO: should we be doing this? - while ((ebc = RB_FIRST(&ec->schedule))) { - RB_REMOVE(&ec->schedule, ebc, glink); - ebc->putref(ebc); - } - gtimer_disarm(&ec->expire); - if (ec->name) free(ec->name); -#if TODO_NOT_IMPLEMENTED - if (ec->sname) free(ec->sname); - if (ec->sid) free(ec->sid); -#endif - free(ec); -} - -epg_channel_t* epg_channel_find_by_uri - ( const char *uri, int create, int *save ) -{ - int save2 = 0; - static epg_object_t *skel = NULL; - if ( !skel ) { - skel = calloc(1, sizeof(epg_channel_t)); - skel->destroy = _epg_channel_destroy; - } - epg_channel_t *ec = (epg_channel_t*) - _epg_object_find_by_uri(uri, create, &save2, - &epg_channels, &skel); - if (save2) { - LIST_INSERT_HEAD(&epg_channel_unmapped, ec, umlink); - *save |= 1; - } - return ec; -} - -epg_channel_t *epg_channel_find_by_id ( uint64_t id ) -{ - return (epg_channel_t*)_epg_object_find_by_id(id, &epg_channels); -} - -int epg_channel_set_name ( epg_channel_t *channel, const char *name ) -{ - int save = 0; - channel_t *ch; - if ( !channel || !name ) return 0; - if ( !channel->name || strcmp(channel->name, name) ) { - if (channel->name) free(channel->name); - channel->name = strdup(name); - // TODO: need to allow overriding of EIT created channels - if ( !channel->channel ) { - LIST_FOREACH(ch, &channel_unmapped, ch_eulink) { - if ( _epg_channel_cmp(channel, ch) ) { - epg_channel_set_channel(channel, ch); - break; - } - } - } - // pass through - if ( channel->channel ) { - save |= channel_rename(channel->channel, name); - } - save |= 1; - } - return save; -} - -int epg_channel_set_icon ( epg_channel_t *channel, const char *icon ) -{ - int save = 0; - if ( !channel | !icon ) return 0; - if ( !channel->icon || strcmp(channel->icon, icon) ) { - if ( channel->icon ) free(channel->icon); - channel->icon = strdup(icon); - // pass through - if ( channel->channel ) { - channel_set_icon(channel->channel, icon); - } - save = 1; - } - return save; -} - -int epg_channel_set_channel ( epg_channel_t *ec, channel_t *ch ) -{ - int save = 0; - if ( !ec ) return 0; - if ( ec->channel != ch ) { - if (ec->channel) { - tvhlog(LOG_DEBUG, "epg", "unlink channels %-30s -> %s", - ec->_.uri, ec->channel->ch_name); - channel_set_epg_source(ec->channel, NULL); - LIST_INSERT_HEAD(&channel_unmapped, ec->channel, ch_eulink); - } else { - LIST_REMOVE(ec, umlink); - } - ec->channel = ch; - if (!ch) { - LIST_INSERT_HEAD(&epg_channel_unmapped, ec, umlink); - ec->_.putref((epg_object_t*)ec); - } else { - tvhlog(LOG_DEBUG, "epg", "link channels %-30s -> %s", - ec->_.uri, ch->ch_name); - channel_set_epg_source(ch, ec); - ec->_.getref((epg_object_t*)ec); - } - save |= 1; - } - return save; + gtimer_disarm(&ch->ch_epg_timer); } epg_broadcast_t *epg_channel_get_broadcast - ( epg_channel_t *channel, time_t start, time_t stop, int create, int *save ) + ( channel_t *ch, time_t start, time_t stop, int create, int *save ) { int save2 = 0; epg_broadcast_t *ebc; static epg_broadcast_t *skel; - if ( !channel || !start || !stop ) return NULL; + if ( !ch || !start || !stop ) return NULL; if ( stop <= start ) return NULL; if ( stop < dispatch_clock ) return NULL; if ( skel == NULL ) skel = calloc(1, sizeof(epg_broadcast_t)); - skel->channel = channel; + skel->channel = ch; skel->start = start; skel->stop = stop; skel->_.id = _epg_object_idx; + skel->_.type = EPG_BROADCAST; skel->_.destroy = _epg_broadcast_destroy; ebc = (epg_broadcast_t*) - _epg_object_find(create, &save2, &channel->schedule, + _epg_object_find(create, &save2, &ch->ch_epg_schedule, (epg_object_t**)&skel, _ebc_win_cmp); if (save2) { ebc->_.getref((epg_object_t*)ebc); /* New current/next */ - if ( (RB_FIRST(&channel->schedule) == (epg_object_t*)ebc) || - (channel->now && - RB_NEXT((epg_object_t*)channel->now, glink) == (epg_object_t*)ebc) ) { - _epg_channel_timer_callback(channel); + if ( (RB_FIRST(&ch->ch_epg_schedule) == (epg_object_t*)ebc) || + (ch->ch_epg_now && + RB_NEXT((epg_object_t*)ch->ch_epg_now, glink) == (epg_object_t*)ebc) ) { + _epg_channel_timer_callback(ch); } *save |= 1; } return ebc; } -htsmsg_t *epg_channel_serialize ( epg_channel_t *channel ) -{ - htsmsg_t *m; - if (!channel || !channel->_.uri) return NULL; - m = htsmsg_create_map(); - htsmsg_add_str(m, "uri", channel->_.uri); - if (channel->name) - htsmsg_add_str(m, "name", channel->name); - if (channel->channel) - htsmsg_add_u32(m, "channel", channel->channel->ch_id); - // TODO: other data - return m; -} - -epg_channel_t *epg_channel_deserialize ( htsmsg_t *m, int create, int *save ) -{ - epg_channel_t *ec; - channel_t *ch; - uint32_t u32; - const char *str; - - if ( !(str = htsmsg_get_str(m, "uri")) ) return NULL; - if ( !(ec = epg_channel_find_by_uri(str, create, save)) ) return NULL; - - if ( (str = htsmsg_get_str(m, "name")) ) - *save |= epg_channel_set_name(ec, str); - - if ( !htsmsg_get_u32(m, "channel", &u32) ) - if ( (ch = channel_find_by_identifier(u32)) ) - epg_channel_set_channel(ec, ch); - // TODO: this call needs updating - - return ec; -} - -/* ************************************************************************** - * Channel mapping - * *************************************************************************/ - -void epg_channel_map_add ( channel_t *ch ) -{ - epg_channel_t *ec; - LIST_FOREACH(ec, &epg_channel_unmapped, umlink) { - if ( _epg_channel_cmp(ec, ch) ) { - epg_channel_set_channel(ec, ch); - return; - } - } - LIST_INSERT_HEAD(&channel_unmapped, ch, ch_eulink); -} - -void epg_channel_map_rem ( channel_t *ch ) -{ - if (ch->ch_epg_channel) { - epg_channel_set_channel(ch->ch_epg_channel, NULL); - } else { - LIST_REMOVE(ch, ch_eulink); - } -} - -void epg_channel_map_mod ( channel_t *ch ) -{ - // If already mapped, ignore - if (!ch->ch_epg_channel) epg_channel_map_add(ch); -} - - /* ************************************************************************** * Querying * *************************************************************************/ @@ -1344,13 +1129,13 @@ static void _eqr_add ( epg_query_result_t *eqr, epg_broadcast_t *e ) } static void _eqr_add_channel - ( epg_query_result_t *eqr, epg_channel_t *ec ) + ( epg_query_result_t *eqr, channel_t *ch ) { epg_object_t *eo; epg_broadcast_t *ebc; - RB_FOREACH(eo, &ec->schedule, glink) { + RB_FOREACH(eo, &ch->ch_epg_schedule, glink) { ebc = (epg_broadcast_t*)eo; - if ( ebc->episode && ebc->channel ) _eqr_add(eqr, ebc); + if ( ebc->episode ) _eqr_add(eqr, ebc); } } @@ -1358,20 +1143,18 @@ void epg_query0 ( epg_query_result_t *eqr, channel_t *channel, channel_tag_t *tag, uint8_t contentgroup, const char *title ) { - epg_object_t *ec; - /* Clear (just incase) */ memset(eqr, 0, sizeof(epg_query_result_t)); /* All channels */ if (!channel) { - RB_FOREACH(ec, &epg_channels, glink) { - _eqr_add_channel(eqr, (epg_channel_t*)ec); + RB_FOREACH(channel, &channel_name_tree, ch_name_link) { + _eqr_add_channel(eqr, channel); } /* Single channel */ - } else if ( channel->ch_epg_channel ) { - _eqr_add_channel(eqr, channel->ch_epg_channel); + } else if ( channel ) { + _eqr_add_channel(eqr, channel); } return; diff --git a/src/epg.h b/src/epg.h index 0e83ae56..16a18f46 100644 --- a/src/epg.h +++ b/src/epg.h @@ -63,6 +63,16 @@ typedef struct epg_broadcast_tree epg_broadcast_tree_t; * Generic Object * ***********************************************************************/ +/* Object type */ +typedef enum epg_object_type +{ + EPG_UNDEF, + EPG_BRAND, + EPG_SEASON, + EPG_EPISODE, + EPG_BROADCAST +} epg_object_type_t; + /* Object */ typedef struct epg_object { @@ -70,8 +80,9 @@ typedef struct epg_object LIST_ENTRY(epg_object) hlink; ///< Global (ID) hash link LIST_ENTRY(epg_object) ulink; ///< Global unref'd link - char *uri; ///< Unique ID (from grabber) + epg_object_type_t type; ///< Specific object type uint64_t id; ///< Internal ID + char *uri; ///< Unique ID (from grabber) int refcount; ///< Reference counting void (*getref) ( epg_object_t* ); ///< Get a reference diff --git a/src/epggrab.c b/src/epggrab.c index cebe43c2..8762f3cb 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -4,10 +4,13 @@ #include "htsmsg.h" #include "settings.h" #include "tvheadend.h" +#include "src/queue.h" #include "epg.h" #include "epggrab.h" -#include "epggrab/pyepg.h" +#include "epggrab/eit.h" #include "epggrab/xmltv.h" +#include "epggrab/pyepg.h" +#include "channels.h" /* Thread protection */ int epggrab_confver; @@ -22,8 +25,8 @@ epggrab_module_t* epggrab_module; epggrab_sched_list_t epggrab_schedule; /* Modules */ -epggrab_module_t* epggrab_module_pyepg; -epggrab_module_t* epggrab_module_xmltv; +LIST_HEAD(epggrab_module_list, epggrab_module); +struct epggrab_module_list epggrab_modules; /* Internal prototypes */ static void* _epggrab_thread ( void* ); @@ -45,8 +48,11 @@ void epggrab_init ( void ) epggrab_module = NULL; // disabled /* Initialise modules */ - epggrab_module_pyepg = pyepg_init(); - epggrab_module_xmltv = xmltv_init(); + LIST_INSERT_HEAD(&epggrab_modules, eit_init(), glink); +#if TODO_XMLTV_SUPPORT + LIST_INSERT_HEAD(&epggrab_modules, xmltv_init(), glink); +#endif + LIST_INSERT_HEAD(&epggrab_modules, pyepg_init(), glink); /* Start thread */ pthread_t tid; @@ -190,11 +196,125 @@ void* _epggrab_thread ( void* p ) epggrab_module_t* epggrab_module_find_by_name ( const char *name ) { - if ( strcmp(name, "pyepg") == 0 ) return epggrab_module_pyepg; - if ( strcmp(name, "xmltv") == 0 ) return epggrab_module_xmltv; + epggrab_module_t *m; + LIST_FOREACH(m, &epggrab_modules, glink) { + if ( !strcmp(m->name(), name) ) return m; + } return NULL; } +/* ************************************************************************** + * 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... + * *************************************************************************/ + +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; +} + +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); + } +} + +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); + } +} + +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); + } +} + +epggrab_channel_t *epggrab_module_channel_create + ( epggrab_channel_tree_t *tree, epggrab_channel_t *iskel ) +{ + 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); + 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); +} + +void epggrab_module_channel_add ( epggrab_channel_tree_t *tree, channel_t *ch ) +{ + epggrab_channel_t *egc; + RB_FOREACH(egc, tree, glink) { + if (_channel_match(egc, ch) ) { + egc->channel = ch; + break; + } + } +} + +void epggrab_module_channel_rem ( epggrab_channel_tree_t *tree, channel_t *ch ) +{ + epggrab_channel_t *egc; + RB_FOREACH(egc, tree, glink) { + if (egc->channel == ch) { + egc->channel = NULL; + break; + } + } +} + +void epggrab_module_channel_mod ( epggrab_channel_tree_t *tree, channel_t *ch ) +{ + epggrab_module_channel_add(tree, ch); +} + /* ************************************************************************** * Configuration handling * *************************************************************************/ diff --git a/src/epggrab.h b/src/epggrab.h index dfc1dba3..dbf961b3 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -27,6 +27,21 @@ typedef struct epggrab_stats epggrab_stats_part_t broadcasts; } epggrab_stats_t; +/* + * Grab channel + */ +typedef struct epggrab_channel +{ + 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; + +RB_HEAD(epggrab_channel_tree, epggrab_channel); +typedef struct epggrab_channel_tree epggrab_channel_tree_t; + /* * Grabber base class */ @@ -40,6 +55,8 @@ typedef struct epggrab_module void (*ch_add) ( struct channel *ch ); void (*ch_rem) ( struct channel *ch ); void (*ch_mod) ( struct channel *ch ); + + LIST_ENTRY(epggrab_module) glink; ///< Global list link } epggrab_module_t; /* @@ -97,4 +114,14 @@ 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_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 ); + +epggrab_channel_t *epggrab_module_channel_create ( epggrab_channel_tree_t *tree, epggrab_channel_t *ch ); +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 34e2c3ae..19beac85 100644 --- a/src/epggrab/eit.c +++ b/src/epggrab/eit.c @@ -43,7 +43,6 @@ void eit_callback ( channel_t *ch, int id, time_t start, time_t stop, const char *extitem, const char *extdesc, const char *exttext ) { int save = 0; - epg_channel_t *ec; epg_broadcast_t *ebc; epg_episode_t *ee; const char *summary = NULL; @@ -53,17 +52,8 @@ void eit_callback ( channel_t *ch, int id, time_t start, time_t stop, /* Disabled? */ //if (epggrab_eit_disabled) return; - /* Channel */ - ec = ch->ch_epg_channel; - if (!ec) { - ec = epg_channel_find_by_uri(ch->ch_name, 1, &save); - if (ec) - epg_channel_set_channel(ec, ch); - } - if (!ec) return; - /* Find broadcast */ - ebc = epg_channel_get_broadcast(ch->ch_epg_channel, start, stop, 1, &save); + ebc = epg_channel_get_broadcast(ch, start, stop, 1, &save); if (!ebc) return; /* Create episode URI */ diff --git a/src/epggrab/eit.h b/src/epggrab/eit.h index 1fc44528..3b0870c4 100644 --- a/src/epggrab/eit.h +++ b/src/epggrab/eit.h @@ -23,7 +23,7 @@ epggrab_module_t *eit_init ( void ); -void eit_callback ( channel_t *ch, int id, time_t start, time_t stop, +void eit_callback ( struct channel *ch, int id, time_t start, time_t stop, const char *title, const char *desc, const char *extitem, const char *extdesc, const char *exttext ); diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c index 79c6e7a6..6c52a911 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -26,6 +26,44 @@ #include "spawn.h" #include "epg.h" #include "epggrab/pyepg.h" +#include "channels.h" + +/* ************************************************************************** + * Channels + * *************************************************************************/ + +epggrab_channel_tree_t _pyepg_channels; + +static channel_t *_pyepg_channel_create ( epggrab_channel_t *skel ) +{ + epggrab_channel_t *ch + = epggrab_module_channel_create(&_pyepg_channels, skel); + 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 ) +{ + epggrab_module_channel_add(&_pyepg_channels, ch); +} + +static void _pyepg_channel_rem ( channel_t *ch ) +{ + epggrab_module_channel_rem(&_pyepg_channels, ch); +} + +static void _pyepg_channel_mod ( channel_t *ch ) +{ + epggrab_module_channel_mod(&_pyepg_channels, ch); +} /* ************************************************************************** * Parsing @@ -47,25 +85,26 @@ static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats ) { int save = 0; htsmsg_t *attr, *tags; - const char *id, *name = NULL; - epg_channel_t *channel; + const char *icon; + channel_t *ch; + epggrab_channel_t skel; if ( data == NULL ) return 0; - if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0; - if ((id = htsmsg_get_str(attr, "id")) == NULL) return 0; - if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; + if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0; + if ((skel.id = (char*)htsmsg_get_str(attr, "id")) == NULL) return 0; + if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; + skel.name = (char*)htsmsg_xml_get_cdata_str(tags, "name"); + icon = htsmsg_xml_get_cdata_str(tags, "image"); - /* Find channel */ - if ((channel = epg_channel_find_by_uri(id, 1, &save)) == NULL) return 0; - stats->channels.total++; - if (save) stats->channels.created++; + ch = _pyepg_channel_create(&skel); - /* Set name */ - name = htsmsg_xml_get_cdata_str(tags, "name"); - if ( name ) save |= epg_channel_set_name(channel, name); - - if (save) stats->channels.modified++; + /* Update values */ + if (ch) { + // TODO: set the name + //if (skel.name) save |= channel_rename(ch, skel.name); + if (icon) channel_set_icon(ch, icon); + } return save; } @@ -245,7 +284,7 @@ static int _pyepg_parse_episode ( htsmsg_t *data, epggrab_stats_t *stats ) } static int _pyepg_parse_broadcast - ( htsmsg_t *data, epg_channel_t *channel, epggrab_stats_t *stats ) + ( htsmsg_t *data, channel_t *channel, epggrab_stats_t *stats ) { int save = 0; htsmsg_t *attr;//, *tags; @@ -289,19 +328,21 @@ static int _pyepg_parse_schedule ( htsmsg_t *data, epggrab_stats_t *stats ) int save = 0; htsmsg_t *attr, *tags; htsmsg_field_t *f; - epg_channel_t *channel; + channel_t *channel; const char *str; if ( data == NULL ) return 0; if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0; if ((str = htsmsg_get_str(attr, "channel")) == NULL) return 0; - if ((channel = epg_channel_find_by_uri(str, 0, NULL)) == NULL) return 0; + if ((channel = _pyepg_channel_find(str)) == NULL) return 0; if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; + stats->channels.total++; HTSMSG_FOREACH(f, tags) { if (strcmp(f->hmf_name, "broadcast") == 0) { - save |= _pyepg_parse_broadcast(htsmsg_get_map_by_field(f), channel, stats); + save |= _pyepg_parse_broadcast(htsmsg_get_map_by_field(f), + channel, stats); } } @@ -333,7 +374,6 @@ static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats ) return save; } -// TODO: add stats updating static int _pyepg_parse ( htsmsg_t *data, epggrab_stats_t *stats ) { htsmsg_t *tags, *epg; @@ -341,8 +381,6 @@ static int _pyepg_parse ( htsmsg_t *data, epggrab_stats_t *stats ) if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; - // TODO: might be a better way to do this using DTD definition? - /* PyEPG format */ if ((epg = htsmsg_get_map(tags, "epg")) != NULL) return _pyepg_parse_epg(epg, stats); @@ -419,6 +457,9 @@ epggrab_module_t* pyepg_init ( void ) 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; return &pyepg_module; } diff --git a/src/epggrab/xmltv.c b/src/epggrab/xmltv.c index 42a4174c..970e8d77 100644 --- a/src/epggrab/xmltv.c +++ b/src/epggrab/xmltv.c @@ -38,6 +38,7 @@ /* ************************************************************************** * Parsing * *************************************************************************/ +#if TODO_XMLTV_GRABBER /** * Hash the description to get a URI for episode @@ -425,3 +426,5 @@ epggrab_module_t* xmltv_init ( void ) xmltv_module.name = _xmltv_name; return &xmltv_module; } + +#endif diff --git a/src/main.c b/src/main.c index 566c103d..ee6c4fc7 100644 --- a/src/main.c +++ b/src/main.c @@ -404,8 +404,8 @@ main(int argc, char **argv) capmt_init(); - epg_init(); epggrab_init(); + epg_init(); dvr_init();