From cfb0179e803080c88a31f978b687e15c03b73ffc Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Thu, 17 May 2012 17:38:35 +0100 Subject: [PATCH] Some more work on getting import from pyepg working. I think I have a slightly better idea how things are likely to work. Current broadcast search is probably a bit noddy, but will do for now. --- src/epg.c | 544 ++++++++++++++++++++++++++++++++++++-------- src/epg.h | 172 ++++++++------ src/epggrab/pyepg.c | 43 ++-- 3 files changed, 562 insertions(+), 197 deletions(-) diff --git a/src/epg.c b/src/epg.c index 64e49a10..6fec7883 100644 --- a/src/epg.c +++ b/src/epg.c @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +// TODO: + #include #include #include @@ -38,6 +40,93 @@ struct epg_season_tree epg_seasons; struct epg_episode_tree epg_episodes; struct epg_channel_tree epg_channels; +/* ************************************************************************** + * Comparators + * *************************************************************************/ + +static int eb_uri_cmp ( const epg_brand_t *a, const epg_brand_t *b ) +{ + return strcmp(a->eb_uri, b->eb_uri); +} + +static int es_uri_cmp ( const epg_season_t *a, const epg_season_t *b ) +{ + return strcmp(a->es_uri, b->es_uri); +} + +static int ee_uri_cmp ( const epg_episode_t *a, const epg_episode_t *b ) +{ + return strcmp(a->ee_uri, b->ee_uri); +} + +static int ec_uri_cmp ( const epg_channel_t *a, const epg_channel_t *b ) +{ + return strcmp(a->ec_uri, b->ec_uri); +} + +static int ebc_win_cmp ( const epg_broadcast_t *a, const epg_broadcast_t *b ) +{ + printf("check a %ld against b %ld to %ld\n", a->eb_start, b->eb_start, b->eb_stop); +// TODO: some le-way? + if ( a->eb_start < b->eb_start ) return -1; + if ( a->eb_start >= b->eb_stop ) return 1; + return 0; +} + +/* ************************************************************************** + * Testing/Debug + * *************************************************************************/ + +static void _epg_dump ( void ) +{ + epg_brand_t *eb; + epg_season_t *es; + epg_episode_t *ee; + epg_channel_t *ec; + epg_broadcast_t *ebc; + printf("dump epg\n"); + + /* Go down the brand/season/episode */ + RB_FOREACH(eb, &epg_brands, eb_link) { + printf("BRAND: %p %s\n", eb, eb->eb_uri); + RB_FOREACH(es, &eb->eb_seasons, es_blink) { + printf(" SEASON: %p %s\n", es, es->es_uri); + RB_FOREACH(ee, &es->es_episodes, ee_slink) { + printf(" EPISODE: %p %s\n", ee, ee->ee_uri); + } + } + RB_FOREACH(ee, &eb->eb_episodes, ee_blink) { + if ( !ee->ee_season ) printf(" EPISODE: %p %s\n", ee, ee->ee_uri); + } + } + RB_FOREACH(es, &epg_seasons, es_link) { + if ( !es->es_brand ) { + printf("SEASON: %p %s\n", es, es->es_uri); + RB_FOREACH(ee, &es->es_episodes, ee_slink) { + printf(" EPISODE: %p %s\n", ee, ee->ee_uri); + } + } + } + RB_FOREACH(ee, &epg_episodes, ee_link) { + if ( !ee->ee_brand && !ee->ee_season ) { + printf("EPISODE: %p %s\n", ee, ee->ee_uri); + } + } + RB_FOREACH(ec, &epg_channels, ec_link) { + printf("CHANNEL: %s\n", ec->ec_uri); + RB_FOREACH(ebc, &ec->ec_schedule, eb_slink) { + if ( ebc->eb_episode ) { + printf(" BROADCAST: %s @ %ld to %ld\n", ebc->eb_episode->ee_uri, + ebc->eb_start, ebc->eb_stop); + } + } + } +} + +/* ************************************************************************** + * Setup / Update + * *************************************************************************/ + void epg_init ( void ) { } @@ -48,80 +137,13 @@ void epg_save ( void ) void epg_updated ( void ) { + _epg_dump(); } -int epg_brand_set_title ( epg_brand_t *brand, const char *title ) -{ - return 0; -} -int epg_brand_set_summary ( epg_brand_t *brand, const char *summary ) -{ - return 0; -} - -int epg_brand_set_season_count ( epg_brand_t *brand, uint16_t count ) -{ - return 0; -} - -int epg_season_set_brand ( epg_season_t *season, epg_brand_t *brand ) -{ - return 0; -} - -int epg_season_set_summary ( epg_season_t *season, const char *summary ) -{ - return 0; -} - -int epg_season_set_episode_count ( epg_season_t *season, uint16_t count ) -{ - return 0; -} - -int epg_season_set_number ( epg_season_t *season, uint16_t number ) -{ - return 0; -} - -int epg_episode_set_title ( epg_episode_t *episode, const char *title ) -{ - return 0; -} -int epg_episode_set_subtitle ( epg_episode_t *episode, const char *title ) -{ - return 0; -} -int epg_episode_set_summary ( epg_episode_t *episode, const char *summary ) -{ - return 0; -} -int epg_episode_set_description ( epg_episode_t *episode, const char *desc ) -{ - return 0; -} -int epg_episode_set_brand ( epg_episode_t *episode, epg_brand_t *brand ) -{ - return 0; -} -int epg_episode_set_season ( epg_episode_t *episode, epg_season_t *season ) -{ - return 0; -} -int epg_episode_set_number ( epg_episode_t *episode, uint16_t number ) -{ - return 0; -} -int epg_episode_set_part ( epg_episode_t *episode, uint16_t part, uint16_t count ) -{ - return 0; -} - -static int eb_uri_cmp ( const epg_brand_t *a, const epg_brand_t *b ) -{ - return strcmp(a->eb_uri, b->eb_uri); -} +/* ************************************************************************** + * Brand + * *************************************************************************/ epg_brand_t* epg_brand_find_by_uri ( const char *id, int create ) { @@ -131,31 +153,123 @@ epg_brand_t* epg_brand_find_by_uri ( const char *id, int create ) lock_assert(&global_lock); // pointless! if ( skel == NULL ) skel = calloc(1, sizeof(epg_brand_t)); + skel->eb_uri = (char*)id; /* Find */ - skel->eb_uri = (char*)id; - eb = RB_FIND(&epg_brands, skel, eb_link, eb_uri_cmp); - if ( eb ) printf("found brand %s @ %p\n", id, eb); + if ( !create ) { + eb = RB_FIND(&epg_brands, skel, eb_link, eb_uri_cmp); /* Create */ - if ( !eb && create ) { + } else { eb = RB_INSERT_SORTED(&epg_brands, skel, eb_link, eb_uri_cmp); if ( eb == NULL ) { eb = skel; skel = NULL; eb->eb_uri = strdup(id); - printf("create brand %s @ %p\n", id, eb); } } return eb; } -static int es_uri_cmp ( const epg_season_t *a, const epg_season_t *b ) +int epg_brand_set_title ( epg_brand_t *brand, const char *title ) { - return strcmp(a->es_uri, b->es_uri); + int save = 0; + if ( !brand || !title ) return 0; + if ( !brand->eb_title || strcmp(brand->eb_title, title) ) { + if ( brand->eb_title ) free(brand->eb_title); + brand->eb_title = strdup(title); + save = 1; + } + return save; } +int epg_brand_set_summary ( epg_brand_t *brand, const char *summary ) +{ + int save = 0; + if ( !brand || !summary ) return 0; + if ( !brand->eb_summary || strcmp(brand->eb_summary, summary) ) { + if ( brand->eb_summary ) free(brand->eb_summary); + brand->eb_summary = strdup(summary); + save = 1; + } + return save; +} + +int epg_brand_set_season_count ( epg_brand_t *brand, uint16_t count ) +{ + // TODO: could set only if less? + int save = 0; + if ( !brand || !count ) return 0; + if ( brand->eb_season_count != count ) { + brand->eb_season_count = count; + save = 1; + } + return save; +} + +int epg_brand_add_season ( epg_brand_t *brand, epg_season_t *season, int u ) +{ + int save = 0; + epg_season_t *es; + if ( !brand || !season ) return 0; + es = RB_INSERT_SORTED(&brand->eb_seasons, season, es_blink, es_uri_cmp); + if ( es == NULL ) { + if ( u ) save |= epg_season_set_brand(season, brand, 0); + save = 1; + } + // TODO?: else assert(es == season) + return save; +} + +int epg_brand_rem_season ( epg_brand_t *brand, epg_season_t *season, int u ) +{ + int save = 0; + epg_season_t *es; + if ( !brand || !season ) return 0; + es = RB_FIND(&brand->eb_seasons, season, es_blink, es_uri_cmp); + if ( es != NULL ) { + // TODO: assert(es == season) + if ( u ) save |= epg_season_set_brand(season, NULL, 0); // TODO: this will do nothing + RB_REMOVE(&brand->eb_seasons, season, es_blink); + save = 1; + } + return save; +} + +int epg_brand_add_episode ( epg_brand_t *brand, epg_episode_t *episode, int u ) +{ + int save = 0; + epg_episode_t *ee; + if ( !brand || !episode ) return 0; + ee = RB_INSERT_SORTED(&brand->eb_episodes, episode, ee_blink, ee_uri_cmp); + if ( ee == NULL ) { + if ( u ) save |= epg_episode_set_brand(episode, brand, 0); + save = 1; + } + // TODO?: else assert(ee == episode) + return save; +} + +int epg_brand_rem_episode ( epg_brand_t *brand, epg_episode_t *episode, int u ) +{ + int save = 0; + epg_episode_t *ee; + if ( !brand || !episode ) return 0; + ee = RB_FIND(&brand->eb_episodes, episode, ee_blink, ee_uri_cmp); + if ( ee != NULL ) { + // TODO?: assert(ee == episode) + if ( u ) save |= epg_episode_set_brand(episode, NULL, 0); // TODO: will do nothing + RB_REMOVE(&brand->eb_episodes, episode, ee_blink); + save = 1; + } + return save; +} + +/* ************************************************************************** + * Season + * *************************************************************************/ + epg_season_t* epg_season_find_by_uri ( const char *id, int create ) { epg_season_t *es; @@ -164,31 +278,108 @@ epg_season_t* epg_season_find_by_uri ( const char *id, int create ) lock_assert(&global_lock); // pointless! if ( skel == NULL ) skel = calloc(1, sizeof(epg_season_t)); + skel->es_uri = (char*)id; /* Find */ - skel->es_uri = (char*)id; - es = RB_FIND(&epg_seasons, skel, es_link, es_uri_cmp); - if ( es ) printf("found season %s @ %p\n", id, es); + if ( !create ) { + es = RB_FIND(&epg_seasons, skel, es_link, es_uri_cmp); /* Create */ - if ( !es && create ) { + } else { es = RB_INSERT_SORTED(&epg_seasons, skel, es_link, es_uri_cmp); if ( es == NULL ) { es = skel; skel = NULL; es->es_uri = strdup(id); - printf("create season %s @ %p\n", id, es); } } return es; } -static int ee_uri_cmp ( const epg_episode_t *a, const epg_episode_t *b ) +int epg_season_set_summary ( epg_season_t *season, const char *summary ) { - return strcmp(a->ee_uri, b->ee_uri); + int save = 0; + if ( !season || !summary ) return 0; + if ( !season->es_summary || strcmp(season->es_summary, summary) ) { + if ( season->es_summary ) free(season->es_summary); + season->es_summary = strdup(summary); + save = 1; + } + return save; } +int epg_season_set_episode_count ( epg_season_t *season, uint16_t count ) +{ + int save = 0; + if ( !season || !count ) return 0; + // TODO: should we only update if number is larger + if ( season->es_episode_count != count ) { + season->es_episode_count = count; + save = 1; + } + return save; +} + +int epg_season_set_number ( epg_season_t *season, uint16_t number ) +{ + int save = 0; + if ( !season || !number ) return 0; + if ( season->es_number != number ) { + season->es_number = number; + save = 1; + } + return save; +} + +int epg_season_set_brand ( epg_season_t *season, epg_brand_t *brand, int u ) +{ + int save = 0; + if ( !season || !brand ) return 0; + if ( season->es_brand != brand ) { + if ( u ) save |= epg_brand_rem_season(season->es_brand, season, 0); + season->es_brand = brand; + if ( u ) save |= epg_brand_add_season(season->es_brand, season, 0); + save = 1; + } + return save; +} + +int epg_season_add_episode + ( epg_season_t *season, epg_episode_t *episode, int u ) +{ + int save = 0; + epg_episode_t *ee; + if ( !season || !episode ) return 0; + ee = RB_INSERT_SORTED(&season->es_episodes, episode, ee_slink, ee_uri_cmp); + if ( ee == NULL ) { + if ( u ) save |= epg_episode_set_season(episode, season, 0); + save = 1; + } + // TODO?: else assert(ee == episode) + return save; +} + +int epg_season_rem_episode + ( epg_season_t *season, epg_episode_t *episode, int u ) +{ + int save = 0; + epg_episode_t *ee; + if ( !season || !episode ) return 0; + ee = RB_FIND(&season->es_episodes, episode, ee_slink, ee_uri_cmp); + if ( ee != NULL ) { + // TODO?: assert(ee == episode) + if ( u ) save |= epg_episode_set_season(episode, NULL, 0); // does nothing + RB_REMOVE(&season->es_episodes, episode, ee_slink); + save = 1; + } + return save; +} + +/* ************************************************************************** + * Episode + * *************************************************************************/ + epg_episode_t* epg_episode_find_by_uri ( const char *id, int create ) { epg_episode_t *ee; @@ -197,62 +388,215 @@ epg_episode_t* epg_episode_find_by_uri ( const char *id, int create ) lock_assert(&global_lock); // pointless! if ( skel == NULL ) skel = calloc(1, sizeof(epg_episode_t)); + skel->ee_uri = (char*)id; /* Find */ - skel->ee_uri = (char*)id; - ee = RB_FIND(&epg_episodes, skel, ee_link, ee_uri_cmp); - if ( ee ) printf("found episode %s @ %p\n", id, ee); + if ( !create ) { + ee = RB_FIND(&epg_episodes, skel, ee_link, ee_uri_cmp); /* Create */ - if ( !ee && create ) { + } else { ee = RB_INSERT_SORTED(&epg_episodes, skel, ee_link, ee_uri_cmp); if ( ee == NULL ) { ee = skel; skel = NULL; ee->ee_uri = strdup(id); - printf("create epsiode %s @ %p\n", id, ee); } } return ee; } -static int ec_uri_cmp ( const epg_channel_t *a, const epg_channel_t *b ) +int epg_episode_set_title ( epg_episode_t *episode, const char *title ) { - return strcmp(a->ec_uri, b->ec_uri); + int save = 0; + if ( !episode || !title ) return 0; + if ( !episode->ee_title || strcmp(episode->ee_title, title) ) { + if ( episode->ee_title ) free(episode->ee_title); + episode->ee_title = strdup(title); + save = 1; + } + return save; } +int epg_episode_set_subtitle ( epg_episode_t *episode, const char *subtitle ) +{ + int save = 0; + if ( !episode || !subtitle ) return 0; + if ( !episode->ee_subtitle || strcmp(episode->ee_subtitle, subtitle) ) { + if ( episode->ee_subtitle ) free(episode->ee_subtitle); + episode->ee_subtitle = strdup(subtitle); + save = 1; + } + return save; +} + +int epg_episode_set_summary ( epg_episode_t *episode, const char *summary ) +{ + int save = 0; + if ( !episode || !summary ) return 0; + if ( !episode->ee_summary || strcmp(episode->ee_summary, summary) ) { + if ( episode->ee_summary ) free(episode->ee_summary); + episode->ee_summary = strdup(summary); + save = 1; + } + return save; +} + +int epg_episode_set_description ( epg_episode_t *episode, const char *desc ) +{ + int save = 0; + if ( !episode || !desc ) return 0; + if ( !episode->ee_description || strcmp(episode->ee_description, desc) ) { + if ( episode->ee_description ) free(episode->ee_description); + episode->ee_description = strdup(desc); + save = 1; + } + return save; +} + +int epg_episode_set_number ( epg_episode_t *episode, uint16_t number ) +{ + int save = 0; + if ( !episode || !number ) return 0; + if ( episode->ee_number != number ) { + episode->ee_number = number; + save = 1; + } + return save; +} + +int epg_episode_set_part ( epg_episode_t *episode, uint16_t part, uint16_t count ) +{ + int save = 0; + // TODO: could treat part/count independently + if ( !episode || !part || !count ) return 0; + if ( episode->ee_part_number != part ) { + episode->ee_part_number = part; + save |= 1; + } + if ( episode->ee_part_count != count ) { + episode->ee_part_count = count; + save |= 1; + } + return save; +} + +int epg_episode_set_brand ( epg_episode_t *episode, epg_brand_t *brand, int u ) +{ + int save = 0; + if ( !episode || !brand ) return 0; + if ( episode->ee_brand != brand ) { + if ( u ) save |= epg_brand_rem_episode(episode->ee_brand, episode, 0); /// does nothing + episode->ee_brand = brand; + if ( u ) save |= epg_brand_add_episode(episode->ee_brand, episode, 0); + save |= 1; + } + return save; +} + +int epg_episode_set_season ( epg_episode_t *episode, epg_season_t *season, int u ) +{ + int save = 0; + if ( !episode || !season ) return 0; + if ( episode->ee_season != season ) { + if ( u ) save |= epg_season_rem_episode(episode->ee_season, episode, 0); + episode->ee_season = season; + if ( u && episode->ee_season ) { + save |= epg_season_add_episode(episode->ee_season, episode, 0); + save |= epg_brand_add_episode(episode->ee_season->es_brand, episode, 0); + // TODO: is this the right place? + } + save = 1; + } + return save; +} + +/* ************************************************************************** + * Broadcast + * *************************************************************************/ + +// Note: will find broadcast playing at this time (not necessarily +// one that starts at this time) +// +// Note: do we need to pass in stop? +epg_broadcast_t* epg_broadcast_find_by_time + ( epg_channel_t *channel, time_t start, time_t stop, int create ) +{ + epg_broadcast_t *eb; + static epg_broadcast_t *skel = NULL; + + if ( !channel || !start || !stop ) return 0; + if ( stop < start ) return 0; + + lock_assert(&global_lock); // pointless! + + if ( skel == NULL ) skel = calloc(1, sizeof(epg_broadcast_t)); + skel->eb_start = start; + skel->eb_stop = stop; + + /* Find */ + if ( !create ) { + eb = RB_FIND(&channel->ec_schedule, skel, eb_slink, ebc_win_cmp); + + /* Create */ + } else { + eb = RB_INSERT_SORTED(&channel->ec_schedule, skel, eb_slink, ebc_win_cmp); + if ( eb == NULL ) { + eb = skel; + skel = NULL; + } + } + + return eb; +} + +int epg_broadcast_set_episode + ( epg_broadcast_t *broadcast, epg_episode_t *episode, int u ) +{ + int save = 0; + if ( !broadcast || !episode ) return 0; + if ( broadcast->eb_episode != episode ) { + broadcast->eb_episode = episode; + //if ( u ) epg_episode_add_broadcast(episode, broadcast, 0); + save = 1; + } + return save; +} + +/* ************************************************************************** + * Channel + * *************************************************************************/ + epg_channel_t* epg_channel_find_by_uri ( const char *id, int create ) { epg_channel_t *ec; static epg_channel_t *skel = NULL; - // TODO: is it really that beneficial to save a few bytes on the stack? - // TODO: does this really need to be the global lock? lock_assert(&global_lock); // pointless! if ( skel == NULL ) skel = calloc(1, sizeof(epg_channel_t)); + skel->ec_uri = (char*)id; /* Find */ - skel->ec_uri = (char*)id; - ec = RB_FIND(&epg_channels, skel, ec_link, ec_uri_cmp); - if ( ec ) printf("found channel %s @ %p\n", id, ec); + if ( !create ) { + ec = RB_FIND(&epg_channels, skel, ec_link, ec_uri_cmp); /* Create */ - if ( !ec && create ) { + } else { ec = RB_INSERT_SORTED(&epg_channels, skel, ec_link, ec_uri_cmp); if ( ec == NULL ) { ec = skel; skel = NULL; ec->ec_uri = strdup(id); - printf("create channel %s @ %p\n", id, ec); } } return ec; } -epg_channel_t* epg_channel_find ( const char *id, const char *name, const char **sname, const int **sid ) +epg_channel_t* epg_channel_find + ( const char *id, const char *name, const char **sname, const int **sid ) { epg_channel_t* channel; diff --git a/src/epg.h b/src/epg.h index 2926bb96..2fdaf905 100644 --- a/src/epg.h +++ b/src/epg.h @@ -34,10 +34,16 @@ struct epg_season; struct epg_episode; struct epg_broadcast; struct epg_channel; -RB_HEAD(epg_brand_tree, epg_brand); -RB_HEAD(epg_season_tree, epg_season); -RB_HEAD(epg_episode_tree, epg_episode); -RB_HEAD(epg_channel_tree, epg_channel); + +/* + * Map types + */ +RB_HEAD(epg_brand_tree, epg_brand); +RB_HEAD(epg_season_tree, epg_season); +RB_HEAD(epg_episode_tree, epg_episode); +RB_HEAD(epg_channel_tree, epg_channel); +RB_HEAD(epg_broadcast_tree, epg_broadcast); +// TODO: not sure above wants to be an RB, prob just LIST /* * Represents a specific show @@ -45,19 +51,18 @@ RB_HEAD(epg_channel_tree, epg_channel); */ typedef struct epg_brand { - RB_ENTRY(epg_brand) eb_link; - uint32_t eb_id; ///< Internal ID - char *eb_uri; ///< Grabber URI - char *eb_title; ///< Brand name - char *eb_summary; ///< Brand summary - uint16_t eb_season_count; ///< Total number of seasons + RB_ENTRY(epg_brand) eb_link; ///< Global list link - LIST_HEAD(, epg_season_t) eb_seasons; ///< Attached seasons - LIST_HEAD(, epg_episode_t) eb_episodes; ///< Un(-seasoned) episodes?? + char *eb_uri; ///< Grabber URI + char *eb_title; ///< Brand name + char *eb_summary; ///< Brand summary + uint16_t eb_season_count; ///< Total number of seasons + + struct epg_season_tree eb_seasons; ///< Season list + struct epg_episode_tree eb_episodes; ///< Episode list // TODO: should this only include unattached episodes? - int eb_refcount; ///< Reference counting - + int eb_refcount; ///< Reference counting } epg_brand_t; /* @@ -65,17 +70,18 @@ typedef struct epg_brand */ typedef struct epg_season { - RB_ENTRY(epg_season) es_link; - uint32_t es_id; ///< Internal ID - char *es_uri; ///< Grabber URI - char *es_summary; ///< Season summary - uint16_t es_number; ///< The season number - uint16_t es_episode_count; ///< Total number of episodes + RB_ENTRY(epg_season) es_link; ///< Global list link + RB_ENTRY(epg_season) es_blink; ///< Brand list link - epg_brand_t *es_brand; ///< Parent brand - LIST_HEAD(, epg_episode_t) es_episodes; ///< Child episodes + char *es_uri; ///< Grabber URI + char *es_summary; ///< Season summary + uint16_t es_number; ///< The season number + uint16_t es_episode_count; ///< Total number of episodes - int es_refcount; ///< Reference counting + epg_brand_t *es_brand; ///< Parent brand + struct epg_episode_tree es_episodes; ///< Episode list + + int es_refcount; ///< Reference counting } epg_season_t; @@ -84,23 +90,26 @@ typedef struct epg_season */ typedef struct epg_episode { - RB_ENTRY(epg_episode) ee_link; - uint32_t ee_id; ///< Internal ID - char *ee_uri; ///< Grabber URI - char *ee_title; ///< Title - char *ee_subtitle; ///< Sub-title - char *ee_summary; ///< Summary - char *ee_description; ///< An extended description - uint8_t ee_genre; ///< Episode genre (TODO: need a list?) - uint16_t ee_number; ///< The episode number - uint16_t ee_part_number; ///< For multipart episodes - uint16_t ee_part_count; ///< For multipart episodes + RB_ENTRY(epg_episode) ee_link; ///< Global link + RB_ENTRY(epg_episode) ee_blink; ///< Brand link + RB_ENTRY(epg_episode) ee_slink; ///< Season link - epg_brand_t *ee_brand; ///< (Grand-)Parent brand - epg_season_t *ee_season; ///< Parent season + char *ee_uri; ///< Grabber URI + char *ee_title; ///< Title + char *ee_subtitle; ///< Sub-title + char *ee_summary; ///< Summary + char *ee_description; ///< An extended description + uint8_t ee_genre; ///< Episode genre + // TODO: genre needs to be a list (should it be a string?) + uint16_t ee_number; ///< The episode number + uint16_t ee_part_number; ///< For multipart episodes + uint16_t ee_part_count; ///< For multipart episodes - int ee_refcount; ///< Reference counting + epg_brand_t *ee_brand; ///< (Grand-)Parent brand + epg_season_t *ee_season; ///< Parent season + struct epg_broadcast_tree ee_broadcasts; ///< Broadcast list + int ee_refcount; ///< Reference counting } epg_episode_t; /* @@ -108,27 +117,33 @@ typedef struct epg_episode */ typedef struct epg_broadcast { - int eb_id; ///< Internal ID - int eb_dvb_id; ///< DVB identifier - time_t eb_start; ///< Start time - time_t eb_stop; ///< End time - epg_episode_t *eb_episode; ///< Episode shown - channel_t* eb_channel; ///< Channel being broadcast on + RB_ENTRY(epg_broadcast) eb_slink; ///< Schedule link + RB_ENTRY(epg_broadcast) eb_elink; ///< Episode link + + int eb_id; ///< Internal ID + int eb_dvb_id; ///< DVB identifier + time_t eb_start; ///< Start time + time_t eb_stop; ///< End time /* Some quality info */ - uint8_t eb_widescreen; ///< Is widescreen - uint8_t eb_hd; ///< Is HD - uint16_t eb_lines; ///< Lines in image (quality) - uint16_t eb_aspect; ///< Aspect ratio (*100) + uint8_t eb_widescreen; ///< Is widescreen + uint8_t eb_hd; ///< Is HD + uint16_t eb_lines; ///< Lines in image (quality) + uint16_t eb_aspect; ///< Aspect ratio (*100) /* Some accessibility support */ - uint8_t eb_deafsigned; ///< In screen signing - uint8_t eb_subtitled; ///< Teletext subtitles - uint8_t eb_audio_desc; ///< Audio description + uint8_t eb_deafsigned; ///< In screen signing + uint8_t eb_subtitled; ///< Teletext subtitles + uint8_t eb_audio_desc; ///< Audio description /* Misc flags */ - uint8_t eb_new; ///< New series / file premiere - uint8_t eb_repeat; ///< Repeat screening + uint8_t eb_new; ///< New series / file premiere + uint8_t eb_repeat; ///< Repeat screening + + epg_episode_t *eb_episode; ///< Episode shown + channel_t *eb_channel; ///< Channel being broadcast on + + int eb_refcount; ///< Reference counting } epg_broadcast_t; /* @@ -136,13 +151,16 @@ typedef struct epg_broadcast */ typedef struct epg_channel { - RB_ENTRY(epg_channel) ec_link; ///< Link to channel map - char *ec_uri; ///< Internal ID (defined by grabbers) - char **ec_sname; ///< DVB service name (for searching) - int **ec_sid; ///< DVB service ids (for searching) - channel_t *ec_channel; ///< Link to real channel - // TODO: further links? - // TODO: reference counter? + RB_ENTRY(epg_channel) ec_link; ///< Global link + + char *ec_uri; ///< Channel URI + char **ec_sname; ///< DVB svc names (to map) + int **ec_sid; ///< DVB svc ids (to map) + + channel_t *ec_channel; ///< Link to real channel + + struct epg_broadcast_tree ec_schedule; ///< Schedule (broadcasts) + } epg_channel_t; /* @@ -178,30 +196,36 @@ void epg_save(void); */ /* Brand set() calls */ -int epg_brand_set_uri ( epg_brand_t *b, const char *uri ) - __attribute__((warn_unused_result)); int epg_brand_set_title ( epg_brand_t *b, const char *title ) __attribute__((warn_unused_result)); int epg_brand_set_summary ( epg_brand_t *b, const char *summary ) - __attribute__((warn_unused_result)); + __attribute__((warn_unused_result)); int epg_brand_set_season_count ( epg_brand_t *b, uint16_t season_count ) - __attribute__((warn_unused_result)); + __attribute__((warn_unused_result)); +int epg_brand_add_season ( epg_brand_t *b, epg_season_t *s, int u ) + __attribute__((warn_unused_result)); +int epg_brand_rem_season ( epg_brand_t *b, epg_season_t *s, int u ) + __attribute__((warn_unused_result)); +int epg_brand_add_episode ( epg_brand_t *b, epg_episode_t *s, int u ) + __attribute__((warn_unused_result)); +int epg_brand_rem_episode ( epg_brand_t *b, epg_episode_t *s, int u ) + __attribute__((warn_unused_result)); /* Season set() calls */ -int epg_season_set_uri ( epg_season_t *s, const char *uri ) - __attribute__((warn_unused_result)); int epg_season_set_summary ( epg_season_t *s, const char *summary ) __attribute__((warn_unused_result)); int epg_season_set_number ( epg_season_t *s, uint16_t number ) __attribute__((warn_unused_result)); int epg_season_set_episode_count ( epg_season_t *s, uint16_t episode_count ) __attribute__((warn_unused_result)); -int epg_season_set_brand ( epg_season_t *s, epg_brand_t *b ) +int epg_season_set_brand ( epg_season_t *s, epg_brand_t *b, int u ) + __attribute__((warn_unused_result)); +int epg_season_add_episode ( epg_season_t *s, epg_episode_t *e, int u ) + __attribute__((warn_unused_result)); +int epg_season_rem_episode ( epg_season_t *s, epg_episode_t *e, int u ) __attribute__((warn_unused_result)); /* Episode set() calls */ -int epg_episode_set_uri ( epg_episode_t *e, const char *uri ) - __attribute__((warn_unused_result)); int epg_episode_set_title ( epg_episode_t *e, const char *title ) __attribute__((warn_unused_result)); int epg_episode_set_subtitle ( epg_episode_t *e, const char *subtitle ) @@ -215,14 +239,14 @@ int epg_episode_set_number ( epg_episode_t *e, uint16_t number ) int epg_episode_set_part ( epg_episode_t *e, uint16_t number, uint16_t count ) __attribute__((warn_unused_result)); -int epg_episode_set_brand ( epg_episode_t *e, epg_brand_t *b ) +int epg_episode_set_brand ( epg_episode_t *e, epg_brand_t *b, int u ) __attribute__((warn_unused_result)); -int epg_episode_set_season ( epg_episode_t *e, epg_season_t *s ) +int epg_episode_set_season ( epg_episode_t *e, epg_season_t *s, int u ) __attribute__((warn_unused_result)); -// Note: you don't need to call set_brand() if you set_season() using a season -// on which you've called set_brand() /* Broadcast set() calls */ +int epg_broadcast_set_episode ( epg_broadcast_t *b, epg_episode_t *e, int u ) + __attribute__((warn_unused_result)); // TODO: need to think how this will work with the new hierarchy void epg_updated ( void ); @@ -240,9 +264,7 @@ epg_season_t *epg_season_find_by_uri ( const char *uri, int create ); epg_episode_t *epg_episode_find_by_uri ( const char *uri, int create ); epg_channel_t *epg_channel_find_by_uri ( const char *uri, int create ); epg_channel_t *epg_channel_find ( const char *uri, const char *name, const char **sname, const int **sid ); -epg_broadcast_t *epg_broadcast_find ( epg_channel_t *ch, epg_episode_t *ep, time_t start, time_t stop, int create ); - -epg_broadcast_t *epg_event_find_by_time(channel_t *ch, time_t t); +epg_broadcast_t *epg_broadcast_find_by_time ( epg_channel_t *ch, time_t start, time_t stop, int create ); epg_broadcast_t *epg_event_find_by_id(int eventid); diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c index 36fd0ed2..2442fed5 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -2,9 +2,11 @@ * PyEPG grabber */ +#define _GNU_SOURCE #include #include #include +#include #include "htsmsg_xml.h" #include "tvheadend.h" #include "spawn.h" @@ -15,10 +17,15 @@ * Parsing * *************************************************************************/ -static int _pyepg_parse_time ( const char *str, time_t *tm ) +static int _pyepg_parse_time ( const char *str, time_t *out ) { - // TODO: implement - return 0; + int ret = 0; + struct tm tm; + if ( strptime(str, "%F %T %z", &tm) != NULL ) { + *out = mktime(&tm); + ret = 1; + } + return ret; } static int _pyepg_parse_channel ( htsmsg_t *data ) @@ -33,7 +40,6 @@ static int _pyepg_parse_channel ( htsmsg_t *data ) 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; - printf("parse_channel(%s)\n", id); /* Find channel */ if ((channel = epg_channel_find(id, name, NULL, NULL)) == NULL) return 0; @@ -55,7 +61,6 @@ static int _pyepg_parse_brand ( htsmsg_t *data ) if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0; if ((str = htsmsg_get_str(attr, "id")) == NULL) return 0; if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; - printf("parse_brand(%s)\n", str); /* Find brand */ if ((brand = epg_brand_find_by_uri(str, 1)) == NULL) return 0; @@ -100,18 +105,15 @@ static int _pyepg_parse_season ( htsmsg_t *data ) if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0; if ((str = htsmsg_get_str(attr, "id")) == NULL) return 0; if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; - printf("parse_season(%s)\n", str); /* Find series */ if ((season = epg_season_find_by_uri(str, 1)) == NULL) return 0; // TODO: do we need to save if created? - printf("have season\n"); /* Set brand */ if ((str = htsmsg_get_str(attr, "brand"))) { - printf("lookup brand(%s)\n", str); if ((brand = epg_brand_find_by_uri(str, 0))) { - save |= epg_season_set_brand(season, brand); + save |= epg_season_set_brand(season, brand, 1); } } @@ -160,26 +162,22 @@ static int _pyepg_parse_episode ( htsmsg_t *data ) if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0; if ((str = htsmsg_get_str(attr, "id")) == NULL) return 0; if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; - printf("parse_episode(%s)\n", str); /* Find episode */ if ((episode = epg_episode_find_by_uri(str, 1)) == NULL) return 0; // TODO: do we need to save if created? - printf("have episode\n"); /* Set brand */ if ((str = htsmsg_get_str(attr, "brand"))) { - printf("lookup brand(%s)\n", str); if ((brand = epg_brand_find_by_uri(str, 0))) { - save |= epg_episode_set_brand(episode, brand); + save |= epg_episode_set_brand(episode, brand, 1); } } /* Set season */ if ((str = htsmsg_get_str(attr, "series"))) { - printf("lookup season(%s)\n", str); if ((season = epg_season_find_by_uri(str, 0))) { - save |= epg_episode_set_season(episode, season); + save |= epg_episode_set_season(episode, season, 1); } } @@ -230,7 +228,6 @@ static int _pyepg_parse_broadcast ( htsmsg_t *data, epg_channel_t *channel ) if ((id = htsmsg_get_str(attr, "episode")) == NULL) return 0; if ((start = htsmsg_get_str(attr, "start")) == NULL ) return 0; if ((stop = htsmsg_get_str(attr, "stop")) == NULL ) return 0; - printf("parse_broadcast(ep=%s, start=%s, stop=%s)\n", id, start, stop); /* Find episode */ if ((episode = epg_episode_find_by_uri(id, 1)) == NULL) return 0; @@ -240,9 +237,14 @@ static int _pyepg_parse_broadcast ( htsmsg_t *data, epg_channel_t *channel ) if (!_pyepg_parse_time(stop, &tm_stop)) return 0; /* Find broadcast */ - // TODO: need to think about this - if ((broadcast = epg_broadcast_find(channel, episode, tm_start, tm_stop, 1)) == NULL) return 0; - save = 1; + printf("%s find broadcast %ld to %ld\n", + channel->ec_uri, tm_start, tm_stop); + broadcast = epg_broadcast_find_by_time(channel, tm_start, tm_stop, 1); + printf("%s broadcast %p\n", channel->ec_uri, broadcast); + if ( broadcast == NULL ) return 0; + + /* Set episode */ + save |= epg_broadcast_set_episode(broadcast, episode, 1); /* TODO: extra metadata */ @@ -261,7 +263,6 @@ static int _pyepg_parse_schedule ( htsmsg_t *data ) if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0; if ((str = htsmsg_get_str(attr, "channel")) == NULL) return 0; - printf("parse_schedule(ch=%s)\n", str); if ((channel = epg_channel_find_by_uri(str, 0)) == NULL) return 0; if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; @@ -279,7 +280,6 @@ static int _pyepg_parse_epg ( htsmsg_t *data ) int save = 0; htsmsg_t *tags; htsmsg_field_t *f; - printf("parse_epg()\n"); if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; @@ -304,7 +304,6 @@ static int _pyepg_parse ( htsmsg_t *data ) { htsmsg_t *tags, *epg; epggrab_module_t *mod; - printf("parse()\n"); if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0;