I think the new generalised approach is working, some stuff was a little too general and some stuff possible is still, but it appears to work!.

This commit is contained in:
Adam Sutton 2012-05-30 21:25:54 +01:00
parent 74cfefd965
commit 43d516464a

429
src/epg.c
View file

@ -241,7 +241,7 @@ void epg_init ( void )
tvhlog(LOG_DEBUG, "epg", " seasons %d", stats.seasons.total);
tvhlog(LOG_DEBUG, "epg", " episodes %d", stats.episodes.total);
tvhlog(LOG_DEBUG, "epg", " broadcasts %d", stats.broadcasts.total);
tvhlog(LOG_DEBUG, "epg", "next object id %lu", _epg_object_idx);
tvhlog(LOG_DEBUG, "epg", "next object id %lu", _epg_object_idx+1);
/* Close file */
munmap(mem, st.st_size);
@ -279,11 +279,22 @@ static void _epg_object_getref ( epg_object_t *eo )
static void _epg_object_putref ( epg_object_t *eo )
{
assert(eo->refcount>0); // Sanity!
assert(eo->refcount>0);
eo->refcount--;
if (!eo->refcount) eo->destroy(eo);
}
static void _epg_object_create ( epg_object_t *eo )
{
if (!eo->id) eo->id = ++_epg_object_idx;
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);
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*))
@ -301,11 +312,7 @@ static epg_object_t *_epg_object_find
*save |= 1;
eo = *skel;
*skel = NULL;
if (!eo->getref) eo->getref = _epg_object_getref;
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_MASK], eo, hlink);
_epg_object_create(eo);
}
}
@ -323,11 +330,9 @@ static epg_object_t *_epg_object_find_by_uri
lock_assert(&global_lock); // pointless!
(*skel)->uri = (char*)uri;
(*skel)->id = _epg_object_idx;
eo = _epg_object_find(create, &save2, tree, skel, _uri_cmp);
if (save2) {
eo->uri = strdup(uri);
*save |= 1;
}
return eo;
@ -353,6 +358,26 @@ static epg_object_t *_epg_object_find_by_id
return NULL;
}
static htsmsg_t * _epg_object_serialize ( epg_object_t *eo )
{
htsmsg_t *m;
if ( !eo->id || !eo->type ) return NULL;
m = htsmsg_create_map();
htsmsg_add_u64(m, "id", eo->id);
htsmsg_add_u32(m, "type", eo->type);
if (eo->uri)
htsmsg_add_str(m, "uri", eo->uri);
return m;
}
static epg_object_t *_epg_object_deserialize ( htsmsg_t *m, epg_object_t *eo )
{
if (htsmsg_get_u64(m, "id", &eo->id)) return NULL;
if (htsmsg_get_u32(m, "type", &eo->type)) return NULL;
eo->uri = (char*)htsmsg_get_str(m, "uri");
return eo;
}
/* **************************************************************************
* Brand
* *************************************************************************/
@ -374,18 +399,24 @@ static void _epg_brand_destroy ( epg_object_t *eo )
free(eb);
}
epg_brand_t* epg_brand_find_by_uri
( const char *uri, int create, int *save )
static epg_object_t **_epg_brand_skel ( void )
{
static epg_object_t *skel = NULL;
if ( !skel ) {
skel = calloc(1, sizeof(epg_brand_t));
skel = calloc(1, sizeof(epg_brand_t));
skel->type = EPG_BRAND;
skel->destroy = _epg_brand_destroy;
}
return &skel;
}
epg_brand_t* epg_brand_find_by_uri
( const char *uri, int create, int *save )
{
return (epg_brand_t*)
_epg_object_find_by_uri(uri, create, save,
&epg_brands, &skel);
&epg_brands,
_epg_brand_skel());
}
epg_brand_t *epg_brand_find_by_id ( uint64_t id )
@ -456,9 +487,7 @@ htsmsg_t *epg_brand_serialize ( epg_brand_t *brand )
{
htsmsg_t *m;
if ( !brand || !brand->_.uri ) return NULL;
m = htsmsg_create_map();
// TODO: generic serialize?
htsmsg_add_str(m, "uri", brand->_.uri);
if ( !(m = _epg_object_serialize((epg_object_t*)brand)) ) return NULL;
if (brand->title)
htsmsg_add_str(m, "title", brand->title);
if (brand->summary)
@ -470,13 +499,13 @@ htsmsg_t *epg_brand_serialize ( epg_brand_t *brand )
epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create, int *save )
{
epg_object_t **skel = _epg_brand_skel();
epg_brand_t *eb;
uint32_t u32;
const char *str;
// TODO: generic deserialize?
if ( !(str = htsmsg_get_str(m, "uri")) ) return NULL;
if ( !(eb = epg_brand_find_by_uri(str, create, save)) ) return NULL;
if ( !_epg_object_deserialize(m, *skel) ) return NULL;
if ( !(eb = epg_brand_find_by_uri((*skel)->uri, create, save)) ) return NULL;
if ( (str = htsmsg_get_str(m, "title")) )
*save |= epg_brand_set_title(eb, str);
@ -508,18 +537,24 @@ static void _epg_season_destroy ( epg_object_t *eo )
free(es);
}
epg_season_t* epg_season_find_by_uri
( const char *uri, int create, int *save )
static epg_object_t **_epg_season_skel ( void )
{
static epg_object_t *skel = NULL;
if ( !skel ) {
skel = calloc(1, sizeof(epg_season_t));
skel = calloc(1, sizeof(epg_season_t));
skel->type = EPG_SEASON;
skel->destroy = _epg_season_destroy;
}
return &skel;
}
epg_season_t* epg_season_find_by_uri
( const char *uri, int create, int *save )
{
return (epg_season_t*)
_epg_object_find_by_uri(uri, create, save,
&epg_seasons, &skel);
&epg_seasons,
_epg_season_skel());
}
epg_season_t *epg_season_find_by_id ( uint64_t id )
@ -594,9 +629,7 @@ htsmsg_t *epg_season_serialize ( epg_season_t *season )
{
htsmsg_t *m;
if (!season || !season->_.uri) return NULL;
m = htsmsg_create_map();
// TODO: generic
htsmsg_add_str(m, "uri", season->_.uri);
if (!(m = _epg_object_serialize((epg_object_t*)season))) return NULL;
if (season->summary)
htsmsg_add_str(m, "summary", season->summary);
if (season->number)
@ -610,14 +643,14 @@ htsmsg_t *epg_season_serialize ( epg_season_t *season )
epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save )
{
epg_object_t **skel = _epg_brand_skel();
epg_season_t *es;
epg_brand_t *eb;
uint32_t u32;
const char *str;
// TODO: generic
if ( !(str = htsmsg_get_str(m, "uri")) ) return NULL;
if ( !(es = epg_season_find_by_uri(str, create, save)) ) return NULL;
if ( !_epg_object_deserialize(m, *skel) ) return NULL;
if ( !(es = epg_season_find_by_uri((*skel)->uri, create, save)) ) return NULL;
if ( (str = htsmsg_get_str(m, "summary")) )
*save |= epg_season_set_summary(es, str);
@ -660,18 +693,24 @@ static void _epg_episode_destroy ( epg_object_t *eo )
free(ee);
}
epg_episode_t* epg_episode_find_by_uri
( const char *uri, int create, int *save )
static epg_object_t **_epg_episode_skel ( void )
{
static epg_object_t *skel = NULL;
if ( !skel ) {
skel = calloc(1, sizeof(epg_episode_t));
skel = calloc(1, sizeof(epg_episode_t));
skel->type = EPG_EPISODE;
skel->destroy = _epg_episode_destroy;
}
return &skel;
}
epg_episode_t* epg_episode_find_by_uri
( const char *uri, int create, int *save )
{
return (epg_episode_t*)
_epg_object_find_by_uri(uri, create, save,
&epg_episodes, &skel);
&epg_episodes,
_epg_episode_skel());
}
epg_episode_t *epg_episode_find_by_id ( uint64_t id )
@ -838,7 +877,7 @@ htsmsg_t *epg_episode_serialize ( epg_episode_t *episode )
{
htsmsg_t *m;
if (!episode || !episode->_.uri) return NULL;
m = htsmsg_create_map();
if (!(m = _epg_object_serialize((epg_object_t*)episode))) return NULL;
htsmsg_add_str(m, "uri", episode->_.uri);
if (episode->title)
htsmsg_add_str(m, "title", episode->title);
@ -863,15 +902,16 @@ htsmsg_t *epg_episode_serialize ( epg_episode_t *episode )
epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
{
epg_object_t **skel = _epg_brand_skel();
epg_episode_t *ee;
epg_season_t *es;
epg_brand_t *eb;
uint32_t u32, u32a;
const char *str;
if ( !(str = htsmsg_get_str(m, "uri")) ) return NULL;
if ( !(ee = epg_episode_find_by_uri(str, create, save)) ) return NULL;
if ( !_epg_object_deserialize(m, *skel) ) return NULL;
if ( !(ee = epg_episode_find_by_uri((*skel)->uri, create, save)) ) return NULL;
if ( (str = htsmsg_get_str(m, "title")) )
*save |= epg_episode_set_title(ee, str);
if ( (str = htsmsg_get_str(m, "subtitle")) )
@ -896,120 +936,12 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
return ee;
}
/* **************************************************************************
* Broadcast
* *************************************************************************/
static void _epg_broadcast_destroy ( epg_object_t *eo )
{
epg_broadcast_t *ebc = (epg_broadcast_t*)eo;
_epg_object_destroy(eo, NULL);
if (ebc->episode) {
_epg_episode_rem_broadcast(ebc->episode, ebc);
ebc->episode->_.putref((epg_object_t*)ebc->episode);
}
free(ebc);
}
epg_broadcast_t* epg_broadcast_find_by_time
( 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, 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;
}
int epg_broadcast_set_episode
( epg_broadcast_t *broadcast, epg_episode_t *episode )
{
int save = 0;
if ( !broadcast || !episode ) return 0;
if ( broadcast->episode != episode ) {
if ( broadcast->episode ) {
_epg_episode_rem_broadcast(broadcast->episode, broadcast);
broadcast->episode->_.putref((epg_object_t*)broadcast->episode);
}
_epg_episode_add_broadcast(episode, broadcast);
broadcast->episode = episode;
episode->_.getref((epg_object_t*)episode);
save = 1;
}
return save;
}
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);
}
htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
{
htsmsg_t *m;
if (!broadcast) return NULL;
if (!broadcast->episode || !broadcast->episode->_.uri) return NULL;
m = htsmsg_create_map();
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_u32(m, "channel", broadcast->channel->ch_id);
htsmsg_add_str(m, "episode", broadcast->episode->_.uri);
return m;
}
epg_broadcast_t *epg_broadcast_deserialize
( htsmsg_t *m, int create, int *save )
{
channel_t *ch;
epg_broadcast_t *ebc;
epg_episode_t *ee;
const char *str;
uint32_t chid, start, stop;
uint64_t id;
// TODO: need to handle broadcasts without a channel
// this will happen for DVR maintained broadcasts which
// reference a no longer used channel (and also they will be
// outside the time limit).
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 ( 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(ch, start, stop, create, save);
if ( !ebc ) return NULL;
*save |= epg_broadcast_set_episode(ebc, ee);
/* Bodge the ID - keep them the same */
ebc->_.id = id;
if ( id >= _epg_object_idx ) _epg_object_idx = id + 1;
// TODO: more metadata
return ebc;
}
/* **************************************************************************
* Channel
* *************************************************************************/
static epg_broadcast_t **_epg_broadcast_skel ( void );
static void _epg_channel_timer_callback ( void *p )
{
time_t next = 0;
@ -1075,39 +1007,192 @@ void epg_channel_unlink ( channel_t *ch )
gtimer_disarm(&ch->ch_epg_timer);
}
static epg_broadcast_t *_epg_channel_add_broadcast
( channel_t *ch, epg_broadcast_t **ebc, int create, int *save )
{
int save2 = 0, timer = 0;
epg_broadcast_t *ret;
/* Find/Create */
ret = (epg_broadcast_t*)
_epg_object_find(create, &save2, &ch->ch_epg_schedule,
(epg_object_t**)ebc, _ebc_win_cmp);
if (!ret) return NULL;
/* Created */
if (save2) {
ret->_.getref((epg_object_t*)ret);
/* New current/next */
if ( (RB_FIRST(&ch->ch_epg_schedule) == (epg_object_t*)ret) ||
(ch->ch_epg_now &&
RB_NEXT((epg_object_t*)ch->ch_epg_now, glink) == (epg_object_t*)ret) ) {
timer = 1;
}
*save |= 1;
/* Update */
} else {
if ( (*ebc)->stop != ret->stop ) {
ret->stop = (*ebc)->stop;
if ( ret == ch->ch_epg_now ) timer = 1;
*save |= 1;
}
if ( (*ebc)->start != ret->start ) {
ret->start = (*ebc)->start;
if ( !ch->ch_epg_now && ret == ch->ch_epg_next ) timer = 1;
}
if ( (*ebc)->episode ) {
*save |= epg_broadcast_set_episode(ret, (*ebc)->episode);
}
}
/* reset timer */
if (timer) _epg_channel_timer_callback(ch);
return ret;
}
epg_broadcast_t *epg_channel_get_broadcast
( 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;
epg_broadcast_t **ebc;
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 = ch;
skel->start = start;
skel->stop = stop;
skel->_.id = _epg_object_idx;
skel->_.type = EPG_BROADCAST;
skel->_.destroy = _epg_broadcast_destroy;
ebc = _epg_broadcast_skel();
(*ebc)->channel = ch;
(*ebc)->start = start;
(*ebc)->stop = stop;
ebc = (epg_broadcast_t*)
_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(&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 _epg_channel_add_broadcast(ch, ebc, create, save);
}
/* **************************************************************************
* Broadcast
* *************************************************************************/
static void _epg_broadcast_destroy ( epg_object_t *eo )
{
epg_broadcast_t *ebc = (epg_broadcast_t*)eo;
_epg_object_destroy(eo, NULL);
if (ebc->episode) {
_epg_episode_rem_broadcast(ebc->episode, ebc);
ebc->episode->_.putref((epg_object_t*)ebc->episode);
}
return ebc;
free(ebc);
}
static epg_broadcast_t **_epg_broadcast_skel ( void )
{
static epg_broadcast_t *skel = NULL;
if ( !skel ) {
skel = calloc(1, sizeof(epg_broadcast_t));
skel->_.type = EPG_BROADCAST;
skel->_.destroy = _epg_broadcast_destroy;
}
return &skel;
}
epg_broadcast_t* epg_broadcast_find_by_time
( 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, 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;
}
int epg_broadcast_set_episode
( epg_broadcast_t *broadcast, epg_episode_t *episode )
{
int save = 0;
if ( !broadcast || !episode ) return 0;
if ( broadcast->episode != episode ) {
if ( broadcast->episode ) {
_epg_episode_rem_broadcast(broadcast->episode, broadcast);
broadcast->episode->_.putref((epg_object_t*)broadcast->episode);
}
_epg_episode_add_broadcast(episode, broadcast);
broadcast->episode = episode;
episode->_.getref((epg_object_t*)episode);
save = 1;
}
return save;
}
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);
}
htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
{
htsmsg_t *m;
if (!broadcast) return NULL;
if (!broadcast->episode || !broadcast->episode->_.uri) return NULL;
if (!(m = _epg_object_serialize((epg_object_t*)broadcast))) return NULL;
htsmsg_add_u32(m, "start", broadcast->start);
htsmsg_add_u32(m, "stop", broadcast->stop);
htsmsg_add_str(m, "episode", broadcast->episode->_.uri);
if (broadcast->channel)
htsmsg_add_u32(m, "channel", broadcast->channel->ch_id);
return m;
}
epg_broadcast_t *epg_broadcast_deserialize
( htsmsg_t *m, int create, int *save )
{
channel_t *ch = NULL;
epg_broadcast_t *ret, **ebc = _epg_broadcast_skel();
epg_episode_t *ee;
const char *str;
uint32_t chid, start, stop;
if ( htsmsg_get_u32(m, "start", &start) ) return NULL;
if ( htsmsg_get_u32(m, "stop", &stop) ) return NULL;
if ( !start || !stop ) return NULL;
if ( stop <= start ) return NULL;
if ( stop < dispatch_clock ) return NULL;
if ( !(str = htsmsg_get_str(m, "episode")) ) return NULL;
if ( !(ee = epg_episode_find_by_uri(str, 0, NULL)) ) return NULL;
/* Set properties */
_epg_object_deserialize(m, (epg_object_t*)*ebc);
(*ebc)->start = start;
(*ebc)->stop = stop;
/* Get channel */
if ( !htsmsg_get_u32(m, "channel", &chid) ) {
ch = channel_find_by_identifier(chid);
}
/* Add to channel */
if ( ch ) {
ret = _epg_channel_add_broadcast(ch, ebc, create, save);
/* Create dangling */
} else {
ret = *ebc;
_epg_object_create((epg_object_t*)*ebc);
*ebc = NULL;
}
/* Set the episode */
*save |= epg_broadcast_set_episode(ret, ee);
return ret;
}
/* **************************************************************************