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).
This commit is contained in:
parent
066b58f6c7
commit
2355e45160
6 changed files with 205 additions and 183 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
268
src/epg.c
268
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);
|
||||
}
|
||||
}
|
||||
|
|
88
src/epg.h
88
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
|
||||
|
||||
|
|
12
src/queue.h
12
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); \
|
||||
|
|
|
@ -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++;
|
||||
|
|
Loading…
Add table
Reference in a new issue