From 2355e45160faf8f3b39a12a443567c30e4f8b89a Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Mon, 11 Jun 2012 17:18:11 +0100 Subject: [PATCH] More significant changes to the EPG code because I want to include the concept of having multiple URIs per object (to cope with badly merged upstream data). --- src/channels.h | 8 +- src/dvr/dvr_autorec.c | 6 +- src/epg.c | 268 ++++++++++++++++++++---------------------- src/epg.h | 88 ++++++++------ src/queue.h | 12 ++ src/webui/extjs.c | 6 +- 6 files changed, 205 insertions(+), 183 deletions(-) diff --git a/src/channels.h b/src/channels.h index b6346cdb..316d3e88 100644 --- a/src/channels.h +++ b/src/channels.h @@ -46,10 +46,10 @@ typedef struct channel { LIST_HEAD(, th_subscription) ch_subscriptions; /* EPG fields */ - epg_object_tree_t ch_epg_schedule; - epg_broadcast_t *ch_epg_now; - epg_broadcast_t *ch_epg_next; - gtimer_t ch_epg_timer; + epg_broadcast_tree_t ch_epg_schedule; + epg_broadcast_t *ch_epg_now; + epg_broadcast_t *ch_epg_next; + gtimer_t ch_epg_timer; gtimer_t ch_epg_timer_head; gtimer_t ch_epg_timer_current; diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 367daecc..f5176094 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -595,14 +595,14 @@ static void dvr_autorec_changed(dvr_autorec_entry_t *dae) { channel_t *ch; - epg_object_t *e; + epg_broadcast_t *e; dvr_autorec_purge_spawns(dae); RB_FOREACH(ch, &channel_name_tree, ch_name_link) { RB_FOREACH(e, &ch->ch_epg_schedule, glink) { - if(autorec_cmp(dae, (epg_broadcast_t*)e)) - dvr_entry_create_by_autorec((epg_broadcast_t*)e, dae); + if(autorec_cmp(dae, e)) + dvr_entry_create_by_autorec(e, dae); } } } diff --git a/src/epg.c b/src/epg.c index 7c8c514f..6c0b4ca6 100644 --- a/src/epg.c +++ b/src/epg.c @@ -39,15 +39,15 @@ #define EPG_HASH_WIDTH 1024 #define EPG_HASH_MASK (EPG_HASH_WIDTH - 1) -/* Element lists */ -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; +/* URI lists */ +epg_object_key_tree_t epg_brands; +epg_object_key_tree_t epg_seasons; +epg_object_key_tree_t epg_episodes; -/* Unreferenced */ -epg_object_list_t epg_object_unref; -epg_object_list_t epg_object_updated; +/* Other special case lists */ +epg_object_list_t epg_objects[EPG_HASH_WIDTH]; +epg_object_list_t epg_object_unref; +epg_object_list_t epg_object_updated; /* Global counter */ static uint64_t _epg_object_idx = 0; @@ -61,9 +61,9 @@ static int _uri_cmp ( const void *a, const void *b ) return strcmp(((epg_object_t*)a)->uri, ((epg_object_t*)b)->uri); } -static int _id_cmp ( const void *a, const void *b ) +static int _key_cmp ( const void *a, const void *b ) { - return ((epg_object_t*)a)->id - ((epg_object_t*)b)->id; + return _uri_cmp(((epg_object_key_t*)a)->obj, ((epg_object_key_t*)b)->obj); } static int _ebc_start_cmp ( const void *a, const void *b ) @@ -99,45 +99,44 @@ static int _epg_write ( int fd, htsmsg_t *m ) return ret; } -static int _epg_write_sect ( int fd, const char *sect ) -{ - htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_str(m, "__section__", sect); - return _epg_write(fd, m); +#define _epg_write_sect(_fd, _total, _sect, _type, _type2, _pack)\ +{\ + int i;\ + epg_object_t *eo;\ + htsmsg_t *m = htsmsg_create_map();\ + htsmsg_add_str(m, "__section__", _sect);\ + if (_epg_write(_fd, m)) return;\ + for ( i = 0; i < EPG_HASH_WIDTH; i++ ) {\ + LIST_FOREACH(eo, &epg_objects[i], hlink) {\ + if (eo->type == _type) {\ + if (_epg_write(_fd, _pack((_type2*)eo))) return;\ + _total++;\ + }\ + }\ + }\ } void epg_save ( void ) { int fd; - epg_object_t *eo; - channel_t *ch; epggrab_stats_t stats; fd = hts_settings_open_file(1, "epgdb"); - memset(&stats, 0, sizeof(stats)); - 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; - stats.brands.total++; - } - if ( _epg_write_sect(fd, "seasons") ) return; - RB_FOREACH(eo, &epg_seasons, glink) { - if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) return; - stats.seasons.total++; - } - if ( _epg_write_sect(fd, "episodes") ) return; - RB_FOREACH(eo, &epg_episodes, glink) { - if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) return; - stats.episodes.total++; - } - if ( _epg_write_sect(fd, "broadcasts") ) return; - 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++; - } - } + + /* Write each object type: + * Note: this is slightly inefficient (due to excessive looping) + * but should be ok */ + _epg_write_sect(fd, stats.brands.total, "brands", EPG_BRAND, + epg_brand_t, epg_brand_serialize); + _epg_write_sect(fd, stats.seasons.total, "seasons", EPG_SEASON, + epg_season_t, epg_season_serialize); + _epg_write_sect(fd, stats.episodes.total, "episodes", EPG_EPISODE, + epg_episode_t, epg_episode_serialize); + _epg_write_sect(fd, stats.broadcasts.total, "broadcasts", EPG_BROADCAST, + epg_broadcast_t, epg_broadcast_serialize); + // TODO: as I include the URI in the object I could use that to allow + // me to only loop over the type lists /* Stats */ tvhlog(LOG_INFO, "epg", "database saved"); @@ -274,11 +273,17 @@ void epg_updated ( void ) * Object * *************************************************************************/ -static void _epg_object_destroy ( epg_object_t *eo, epg_object_tree_t *tree ) +static void _epg_object_destroy + ( epg_object_t *eo, epg_object_key_tree_t *tree ) { + epg_object_key_t *ek; assert(eo->refcount == 0); if (eo->uri) free(eo->uri); - if (tree) RB_REMOVE(tree, eo, glink); + if (tree) { + LIST_FOREACH(ek, &eo->aliases, olink) { + RB_REMOVE(tree, ek, glink); + } + } if (eo->_updated) LIST_REMOVE(eo, uplink); LIST_REMOVE(eo, hlink); } @@ -310,56 +315,47 @@ static void _epg_object_create ( epg_object_t *eo ) else if (eo->id > _epg_object_idx) _epg_object_idx = eo->id; if (!eo->getref) eo->getref = _epg_object_getref; if (!eo->putref) eo->putref = _epg_object_putref; - if (eo->uri) eo->uri = strdup(eo->uri); _epg_object_set_updated(eo); LIST_INSERT_HEAD(&epg_object_unref, eo, ulink); LIST_INSERT_HEAD(&epg_objects[eo->id & EPG_HASH_MASK], eo, hlink); } -static epg_object_t *_epg_object_find - ( int create, int *save, epg_object_tree_t *tree, - epg_object_t **skel, int (*cmp) (const void*,const void*)) -{ - epg_object_t *eo; - - /* Find */ - if ( !create ) { - eo = RB_FIND(tree, *skel, glink, cmp); - - /* Create */ - } else { - eo = RB_INSERT_SORTED(tree, *skel, glink, cmp); - if ( eo == NULL ) { - *save |= 1; - eo = *skel; - *skel = NULL; - _epg_object_create(eo); - } - } - - return eo; -} - static epg_object_t *_epg_object_find_by_uri ( const char *uri, int create, int *save, - epg_object_tree_t *tree, epg_object_t **skel ) + epg_object_key_tree_t *tree, epg_object_t **skel ) { - int save2 = 0; - epg_object_t *eo; + static epg_object_key_t *ekskel = NULL; + epg_object_key_t *ek; assert(skel != NULL); - lock_assert(&global_lock); // pointless! + lock_assert(&global_lock); - (*skel)->uri = (char*)uri; + if (!ekskel) ekskel = calloc(1, sizeof(epg_object_key_t)); + ekskel->uri = (char*)uri; - eo = _epg_object_find(create, &save2, tree, skel, _uri_cmp); - if (save2) { - *save |= 1; + /* Find only */ + if ( !create ) { + ek = RB_FIND(tree, ekskel, glink, _key_cmp); + + /* Find/create */ + } else { + ek = RB_INSERT_SORTED(tree, ekskel, glink, _key_cmp); + if ( !ek ) { + *save = 1; + ek = ekskel; + ek->uri = strdup(uri); + ek->obj = *skel; + ek->obj->uri = strdup(uri); // TODO: could not dup this! + LIST_INSERT_HEAD(&ek->obj->aliases, ek, olink); + _epg_object_create(ek->obj); + *skel = NULL; + ekskel = NULL; + } } - return eo; + return ek ? ek->obj : NULL; } -static epg_object_t *_epg_object_find_by_id2 ( uint64_t id ) +static epg_object_t *_epg_object_find_by_id ( uint64_t id ) { epg_object_t *eo; LIST_FOREACH(eo, &epg_objects[id & EPG_HASH_MASK], hlink) { @@ -368,17 +364,6 @@ static epg_object_t *_epg_object_find_by_id2 ( uint64_t id ) return NULL; } -static epg_object_t *_epg_object_find_by_id - ( uint64_t id, epg_object_tree_t *tree ) -{ - epg_object_t *eo; - if (!tree) return _epg_object_find_by_id2(id); - RB_FOREACH(eo, tree, glink) { - if ( eo->id == id ) return eo; - } - return NULL; -} - static htsmsg_t * _epg_object_serialize ( epg_object_t *eo ) { htsmsg_t *m; @@ -421,11 +406,11 @@ static int _epg_object_set_str static void _epg_brand_destroy ( epg_object_t *eo ) { epg_brand_t *eb = (epg_brand_t*)eo; - if (RB_FIRST(&eb->seasons)) { + if (LIST_FIRST(&eb->seasons)) { tvhlog(LOG_CRIT, "epg", "attempt to destroy brand with seasons"); assert(0); } - if (RB_FIRST(&eb->episodes)) { + if (LIST_FIRST(&eb->episodes)) { tvhlog(LOG_CRIT, "epg", "attempt to destroy brand with episodes"); assert(0); } @@ -463,7 +448,7 @@ epg_brand_t* epg_brand_find_by_uri epg_brand_t *epg_brand_find_by_id ( uint64_t id ) { - return (epg_brand_t*)_epg_object_find_by_id(id, &epg_brands); + return (epg_brand_t*)_epg_object_find_by_id(id); } int epg_brand_set_title ( epg_brand_t *brand, const char *title ) @@ -496,28 +481,28 @@ int epg_brand_set_season_count ( epg_brand_t *brand, uint16_t count ) static void _epg_brand_add_season ( epg_brand_t *brand, epg_season_t *season ) { - RB_INSERT_SORTED(&brand->seasons, season, blink, _uri_cmp); + LIST_INSERT_HEAD(&brand->seasons, season, blink); _epg_object_set_updated((epg_object_t*)brand); } static void _epg_brand_rem_season ( epg_brand_t *brand, epg_season_t *season ) { - RB_REMOVE(&brand->seasons, season, blink); + LIST_REMOVE(season, blink); _epg_object_set_updated((epg_object_t*)brand); } static void _epg_brand_add_episode ( epg_brand_t *brand, epg_episode_t *episode ) { - RB_INSERT_SORTED(&brand->episodes, episode, blink, _uri_cmp); + LIST_INSERT_HEAD(&brand->episodes, episode, blink); _epg_object_set_updated((epg_object_t*)brand); } static void _epg_brand_rem_episode ( epg_brand_t *brand, epg_episode_t *episode ) { - RB_REMOVE(&brand->episodes, episode, blink); + LIST_REMOVE(episode, blink); _epg_object_set_updated((epg_object_t*)brand); } @@ -557,12 +542,17 @@ epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create, int *save ) htsmsg_t *epg_brand_list ( void ) { + int i; epg_object_t *eo; htsmsg_t *a, *e; a = htsmsg_create_list(); - RB_FOREACH(eo, &epg_brands, glink) { - e = epg_brand_serialize((epg_brand_t*)eo); - htsmsg_add_msg(a, NULL, e); + for ( i = 0; i < EPG_HASH_WIDTH; i++ ) { + LIST_FOREACH(eo, &epg_objects[i], hlink) { + if (eo->type == EPG_BRAND) { + e = epg_brand_serialize((epg_brand_t*)eo); + htsmsg_add_msg(a, NULL, e); + } + } } return a; } @@ -574,7 +564,7 @@ htsmsg_t *epg_brand_list ( void ) static void _epg_season_destroy ( epg_object_t *eo ) { epg_season_t *es = (epg_season_t*)eo; - if (RB_FIRST(&es->episodes)) { + if (LIST_FIRST(&es->episodes)) { tvhlog(LOG_CRIT, "epg", "attempt to destory season with episodes"); assert(0); } @@ -615,7 +605,7 @@ epg_season_t* epg_season_find_by_uri epg_season_t *epg_season_find_by_id ( uint64_t id ) { - return (epg_season_t*)_epg_object_find_by_id(id, &epg_seasons); + return (epg_season_t*)_epg_object_find_by_id(id); } int epg_season_set_summary ( epg_season_t *season, const char *summary ) @@ -673,14 +663,14 @@ int epg_season_set_brand ( epg_season_t *season, epg_brand_t *brand, int u ) static void _epg_season_add_episode ( epg_season_t *season, epg_episode_t *episode ) { - RB_INSERT_SORTED(&season->episodes, episode, slink, _uri_cmp); + LIST_INSERT_HEAD(&season->episodes, episode, slink); _epg_object_set_updated((epg_object_t*)season); } static void _epg_season_rem_episode ( epg_season_t *season, epg_episode_t *episode ) { - RB_REMOVE(&season->episodes, episode, slink); + LIST_REMOVE(episode, slink); _epg_object_set_updated((epg_object_t*)season); } @@ -732,7 +722,7 @@ epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save ) static void _epg_episode_destroy ( epg_object_t *eo ) { epg_episode_t *ee = (epg_episode_t*)eo; - if (RB_FIRST(&ee->broadcasts)) { + if (LIST_FIRST(&ee->broadcasts)) { tvhlog(LOG_CRIT, "epg", "attempt to destroy episode with broadcasts"); assert(0); } @@ -779,7 +769,7 @@ epg_episode_t* epg_episode_find_by_uri epg_episode_t *epg_episode_find_by_id ( uint64_t id ) { - return (epg_episode_t*)_epg_object_find_by_id(id, &epg_episodes); + return (epg_episode_t*)_epg_object_find_by_id(id); } int epg_episode_set_title ( epg_episode_t *episode, const char *title ) @@ -919,14 +909,14 @@ int epg_episode_set_genre_str ( epg_episode_t *ee, const char **gstr ) static void _epg_episode_add_broadcast ( epg_episode_t *episode, epg_broadcast_t *broadcast ) { - RB_INSERT_SORTED(&episode->broadcasts, broadcast, elink, _id_cmp); + LIST_INSERT_SORTED(&episode->broadcasts, broadcast, elink, _ebc_start_cmp); _epg_object_set_updated((epg_object_t*)episode); } static void _epg_episode_rem_broadcast ( epg_episode_t *episode, epg_broadcast_t *broadcast ) { - RB_REMOVE(&episode->broadcasts, broadcast, elink); + LIST_REMOVE(broadcast, elink); _epg_object_set_updated((epg_object_t*)episode); } @@ -1034,7 +1024,6 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save ) static void _epg_channel_timer_callback ( void *p ) { time_t next = 0; - epg_object_t *eo; epg_broadcast_t *ebc, *cur; channel_t *ch = (channel_t*)p; @@ -1043,15 +1032,14 @@ static void _epg_channel_timer_callback ( void *p ) ch->ch_epg_now = ch->ch_epg_next = NULL; /* Check events */ - while ( (eo = RB_FIRST(&ch->ch_epg_schedule)) ) { - ebc = (epg_broadcast_t*)eo; + while ( (ebc = RB_FIRST(&ch->ch_epg_schedule)) ) { /* Expire */ if ( ebc->stop <= dispatch_clock ) { - RB_REMOVE(&ch->ch_epg_schedule, eo, glink); + RB_REMOVE(&ch->ch_epg_schedule, ebc, glink); tvhlog(LOG_DEBUG, "epg", "expire event %lu from %s", - eo->id, ch->ch_name); - eo->putref(eo); + ebc->_.id, ch->ch_name); + ebc->_.putref((epg_object_t*)ebc); continue; // skip to next /* No now */ @@ -1062,7 +1050,7 @@ static void _epg_channel_timer_callback ( void *p ) /* Now/Next */ } else { ch->ch_epg_now = ebc; - ch->ch_epg_next = (epg_broadcast_t*)RB_NEXT(eo, glink); + ch->ch_epg_next = RB_NEXT(ebc, glink); next = ebc->stop; } break; @@ -1091,7 +1079,7 @@ static void _epg_channel_rem_broadcast ( channel_t *ch, epg_broadcast_t *ebc, epg_broadcast_t *new ) { if (new) dvr_event_replaced(ebc, new); - RB_REMOVE(&ch->ch_epg_schedule, (epg_object_t*)ebc, glink); + RB_REMOVE(&ch->ch_epg_schedule, ebc, glink); ebc->_.putref((epg_object_t*)ebc); } @@ -1099,18 +1087,28 @@ static epg_broadcast_t *_epg_channel_add_broadcast ( channel_t *ch, epg_broadcast_t **bcast, int create, int *save ) { int save2 = 0, timer = 0; - epg_object_t *eo; epg_broadcast_t *ebc, *ret; /* Set channel */ (*bcast)->channel = ch; - /* Find/Create */ - eo = _epg_object_find(create, &save2, &ch->ch_epg_schedule, - (epg_object_t**)bcast, _ebc_start_cmp); - if (!eo) return NULL; - ret = (epg_broadcast_t*)eo; + /* Find (only) */ + if ( !create ) { + ret = RB_FIND(&ch->ch_epg_schedule, *bcast, glink, _ebc_start_cmp); + + /* Find/Create */ + } else { + ret = RB_INSERT_SORTED(&ch->ch_epg_schedule, *bcast, glink, _ebc_start_cmp); + if (!ret) { + save2 = 1; + ret = *bcast; + *bcast = NULL; + _epg_object_create((epg_object_t*)ret); + } + } + if (!ret) return NULL; + /* No changes */ if (!save2 && (ret->start == (*bcast)->start) && (ret->stop == (*bcast)->stop)) return ret; @@ -1123,23 +1121,23 @@ static epg_broadcast_t *_epg_channel_add_broadcast *save |= 1; /* Remove overlapping (before) */ - while ( (ebc = (epg_broadcast_t*)RB_PREV(eo, glink)) != NULL ) { + while ( (ebc = RB_PREV(ret, glink)) != NULL ) { if ( ebc->stop <= ret->start ) break; if ( ch->ch_epg_now == ebc ) ch->ch_epg_now = NULL; _epg_channel_rem_broadcast(ch, ebc, ret); } /* Remove overlapping (after) */ - while ( (ebc = (epg_broadcast_t*)RB_NEXT(eo, glink)) != NULL ) { + while ( (ebc = RB_NEXT(ret, glink)) != NULL ) { if ( ebc->start >= ret->stop ) break; _epg_channel_rem_broadcast(ch, ebc, ret); } /* Check now/next change */ - if ( RB_FIRST(&ch->ch_epg_schedule) == eo ) { + if ( RB_FIRST(&ch->ch_epg_schedule) == ret ) { timer = 1; } else if ( ch->ch_epg_now && - RB_NEXT((epg_object_t*)ch->ch_epg_now, glink) == eo ) { + RB_NEXT(ch->ch_epg_now, glink) == ret ) { timer = 1; } @@ -1150,9 +1148,9 @@ static epg_broadcast_t *_epg_channel_add_broadcast void epg_channel_unlink ( channel_t *ch ) { - epg_object_t *eo; - while ( (eo = RB_FIRST(&ch->ch_epg_schedule)) ) { - _epg_channel_rem_broadcast(ch, (epg_broadcast_t*)eo, NULL); + epg_broadcast_t *ebc; + while ( (ebc = RB_FIRST(&ch->ch_epg_schedule)) ) { + _epg_channel_rem_broadcast(ch, ebc, NULL); } gtimer_disarm(&ch->ch_epg_timer); } @@ -1207,13 +1205,9 @@ epg_broadcast_t* epg_broadcast_find_by_time epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id, channel_t *ch ) { - epg_object_t *eo = NULL; - if ( ch ) { - eo = _epg_object_find_by_id(id, &ch->ch_epg_schedule); - } else { - eo = _epg_object_find_by_id2(id); - } - return (epg_broadcast_t*)eo; + // TODO: channel left in for now in case I decide to change implementation + // to simplify the search! + return (epg_broadcast_t*)_epg_object_find_by_id(id); } int epg_broadcast_set_episode @@ -1238,7 +1232,7 @@ int epg_broadcast_set_episode epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *broadcast ) { if ( !broadcast ) return NULL; - return (epg_broadcast_t*)RB_NEXT((epg_object_t*)broadcast, glink); + return RB_NEXT(broadcast, glink); } htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast ) @@ -1524,10 +1518,8 @@ static void _eqr_add_channel ( epg_query_result_t *eqr, channel_t *ch, uint8_t genre, regex_t *preg, time_t start ) { - epg_object_t *eo; epg_broadcast_t *ebc; - RB_FOREACH(eo, &ch->ch_epg_schedule, glink) { - ebc = (epg_broadcast_t*)eo; + RB_FOREACH(ebc, &ch->ch_epg_schedule, glink) { if ( ebc->episode ) _eqr_add(eqr, ebc, genre, preg, start); } } diff --git a/src/epg.h b/src/epg.h index 77c30dfa..a15c293e 100644 --- a/src/epg.h +++ b/src/epg.h @@ -30,32 +30,48 @@ struct channel_tag; /* * Map types */ -LIST_HEAD(epg_object_list, epg_object); -RB_HEAD(epg_object_tree, epg_object); -RB_HEAD(epg_brand_tree, epg_brand); -RB_HEAD(epg_season_tree, epg_season); -RB_HEAD(epg_episode_tree, epg_episode); -RB_HEAD(epg_broadcast_tree, epg_broadcast); +LIST_HEAD(epg_object_list, epg_object); +LIST_HEAD(epg_object_key_list, epg_object_key); +RB_HEAD (epg_object_key_tree, epg_object_key); +LIST_HEAD(epg_brand_list, epg_brand); +LIST_HEAD(epg_season_list, epg_season); +LIST_HEAD(epg_episode_list, epg_episode); +LIST_HEAD(epg_broadcast_list, epg_broadcast); +RB_HEAD (epg_broadcast_tree, epg_broadcast); /* - * Typedefs + * Typedefs (most are redundant!) */ -typedef struct epg_object epg_object_t; -typedef struct epg_brand epg_brand_t; -typedef struct epg_season epg_season_t; -typedef struct epg_episode epg_episode_t; -typedef struct epg_broadcast epg_broadcast_t; -typedef struct epg_object_list epg_object_list_t; -typedef struct epg_object_tree epg_object_tree_t; -typedef struct epg_brand_tree epg_brand_tree_t; -typedef struct epg_season_tree epg_season_tree_t; -typedef struct epg_episode_tree epg_episode_tree_t; -typedef struct epg_broadcast_tree epg_broadcast_tree_t; +typedef struct epg_object epg_object_t; +typedef struct epg_object_key epg_object_key_t; +typedef struct epg_brand epg_brand_t; +typedef struct epg_season epg_season_t; +typedef struct epg_episode epg_episode_t; +typedef struct epg_broadcast epg_broadcast_t; +typedef struct epg_season_list epg_season_list_t; +typedef struct epg_episode_list epg_episode_list_t; +typedef struct epg_broadcast_list epg_broadcast_list_t; +typedef struct epg_broadcast_tree epg_broadcast_tree_t; +typedef struct epg_object_list epg_object_list_t; +typedef struct epg_object_key_tree epg_object_key_tree_t; +typedef struct epg_object_key_list epg_object_key_list_t; + /* ************************************************************************ * Generic Object * ***********************************************************************/ +/* Used to allow multiple keys per object */ +typedef struct epg_object_key +{ + LIST_ENTRY(epg_object_key) olink; ///< Link to object's list + RB_ENTRY(epg_object_key) glink; ///< Link to global list + + epg_object_t *obj; ///< Object we're wrapping + const char *uri; ///< Object URI + // TODO: could make this union? +} epg_object_key_t; + /* Object type */ typedef enum epg_object_type { @@ -69,16 +85,17 @@ typedef enum epg_object_type /* Object */ struct epg_object { - RB_ENTRY(epg_object) glink; ///< Global (URI) list link - LIST_ENTRY(epg_object) hlink; ///< Global (ID) hash link - LIST_ENTRY(epg_object) ulink; ///< Global unref'd link - LIST_ENTRY(epg_object) uplink; ///< Global updated link + LIST_ENTRY(epg_object) hlink; ///< Global (ID) link + LIST_ENTRY(epg_object) ulink; ///< Global unref'd link + LIST_ENTRY(epg_object) uplink; ///< Global updated link + + epg_object_type_t type; ///< Specific object type + uint64_t id; ///< Internal ID + char *uri; ///< Unique ID (from grabber) + epg_object_key_list_t aliases; ///< URI aliases (inc primary) - epg_object_type_t type; ///< Specific object type - uint64_t id; ///< Internal ID - char *uri; ///< Unique ID (from grabber) - int refcount; ///< Reference counting - int _updated; ///< Flag to indicate updated + int _updated; ///< Flag to indicate updated + int refcount; ///< Reference counting // Note: could use LIST_ENTRY field to determine this! void (*getref) ( epg_object_t* ); ///< Get a reference @@ -102,8 +119,8 @@ struct epg_brand uint16_t season_count; ///< Total number of seasons char *image; ///< Brand image - epg_season_tree_t seasons; ///< Season list - epg_episode_tree_t episodes; ///< Episode list + epg_season_list_t seasons; ///< Season list + epg_episode_list_t episodes; ///< Episode list }; /* Lookup */ @@ -142,9 +159,9 @@ struct epg_season uint16_t episode_count; ///< Total number of episodes char *image; ///< Season image - RB_ENTRY(epg_season) blink; ///< Brand list link + LIST_ENTRY(epg_season) blink; ///< Brand list link epg_brand_t *brand; ///< Parent brand - epg_episode_tree_t episodes; ///< Episode list + epg_episode_list_t episodes; ///< Episode list }; @@ -189,11 +206,11 @@ struct epg_episode uint16_t part_count; ///< For multipart episodes char *image; ///< Episode image - RB_ENTRY(epg_episode) blink; ///< Brand link - RB_ENTRY(epg_episode) slink; ///< Season link + LIST_ENTRY(epg_episode) blink; ///< Brand link + LIST_ENTRY(epg_episode) slink; ///< Season link epg_brand_t *brand; ///< (Grand-)Parent brand epg_season_t *season; ///< Parent season - epg_broadcast_tree_t broadcasts; ///< Broadcast list + epg_broadcast_list_t broadcasts; ///< Broadcast list }; @@ -281,7 +298,8 @@ struct epg_broadcast uint8_t is_new; ///< New series / file premiere uint8_t is_repeat; ///< Repeat screening - RB_ENTRY(epg_broadcast) elink; ///< Episode link + RB_ENTRY(epg_broadcast) glink; ///< Schedule link + LIST_ENTRY(epg_broadcast) elink; ///< Episode link epg_episode_t *episode; ///< Episode shown struct channel *channel; ///< Channel being broadcast on diff --git a/src/queue.h b/src/queue.h index bf5a4de0..fdb78e90 100644 --- a/src/queue.h +++ b/src/queue.h @@ -111,6 +111,18 @@ } \ } while(0) +#define LIST_FIND(head, skel, field, cmpfunc)\ +({\ + typeof(skel) a, b = NULL;\ + LIST_FOREACH(a, head, field) {\ + if (!cmpfunc(skel, a)) {\ + b = a;\ + break;\ + }\ + }\ + b;\ +}) + #define TAILQ_INSERT_SORTED(head, elm, field, cmpfunc) do { \ if(TAILQ_FIRST(head) == NULL) { \ TAILQ_INSERT_HEAD(head, elm, field); \ diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 55d00e28..2054f32d 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -767,7 +767,7 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque) /* Alternative broadcasts */ if (!strcmp(type, "alternative")) { - RB_FOREACH(ebc, &ee->broadcasts, elink) { + LIST_FOREACH(ebc, &ee->broadcasts, elink) { ch = ebc->channel; if ( !ch ) continue; // skip something not viewable if ( ebc == e ) continue; // skip self @@ -783,7 +783,7 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque) /* Related */ } else if (!strcmp(type, "related")) { if (ee->brand) { - RB_FOREACH(ee2, &ee->brand->episodes, blink) { + LIST_FOREACH(ee2, &ee->brand->episodes, blink) { if (ee2 == ee) continue; if (!ee2->title) continue; count++; @@ -796,7 +796,7 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_msg(array, NULL, m); } } else if (ee->season) { - RB_FOREACH(ee2, &ee->season->episodes, slink) { + LIST_FOREACH(ee2, &ee->season->episodes, slink) { if (ee2 == ee) continue; if (!ee2->title) continue; count++;