diff --git a/src/epg.c b/src/epg.c index d2864325..7fd851b9 100644 --- a/src/epg.c +++ b/src/epg.c @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -// TODO: - #include #include #include @@ -66,8 +64,6 @@ static int ec_uri_cmp ( const epg_channel_t *a, const epg_channel_t *b ) 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; @@ -142,7 +138,7 @@ void epg_save ( void ) void epg_updated ( void ) { - _epg_dump(); + if (0)_epg_dump(); } @@ -546,6 +542,21 @@ int epg_episode_rem_broadcast return save; } +int epg_episode_get_number_onscreen + ( epg_episode_t *episode, char *buf, int len ) +{ + int i = 0; + if ( episode->ee_number ) { + // TODO: add counts + if ( episode->ee_season && episode->ee_season->es_number ) { + i += snprintf(&buf[i], len-i, "Season %d ", + episode->ee_season->es_number); + } + i += snprintf(&buf[i], len-i, "Episode %d", episode->ee_number); + } + return i; +} + /* ************************************************************************** * Broadcast * *************************************************************************/ @@ -566,8 +577,9 @@ epg_broadcast_t* epg_broadcast_find_by_time lock_assert(&global_lock); // pointless! if ( skel == NULL ) skel = calloc(1, sizeof(epg_broadcast_t)); - skel->eb_start = start; - skel->eb_stop = stop; + skel->eb_channel = channel; + skel->eb_start = start; + skel->eb_stop = stop; /* Find */ if ( !create ) { @@ -592,7 +604,7 @@ int epg_broadcast_set_episode if ( !broadcast || !episode ) return 0; if ( broadcast->eb_episode != episode ) { broadcast->eb_episode = episode; - //if ( u ) epg_episode_add_broadcast(episode, broadcast, 0); + if ( u ) save |= epg_episode_add_broadcast(episode, broadcast, 0); save = 1; } return save; @@ -629,38 +641,56 @@ epg_channel_t* epg_channel_find_by_uri ( const char *id, int create ) return ec; } -epg_channel_t* epg_channel_find - ( const char *id, const char *name, const char **sname, const int **sid ) +int epg_channel_set_name ( epg_channel_t *channel, const char *name ) { - epg_channel_t* channel; - - /* Find or create */ - if ((channel = epg_channel_find_by_uri(id, 1)) == NULL) return NULL; - - /* Update fields? */ - - return channel; + int save = 0; + if ( !channel || !name ) return 0; + if ( !channel->ec_name || strcmp(channel->ec_name, name) ) { + channel->ec_name = strdup(name); + // TODO: lookup real channel + save = 1; + } + return save; } /* ************************************************************************** * Querying * *************************************************************************/ +static void _eqr_add ( epg_query_result_t *eqr, epg_broadcast_t *e ) +{ + /* More space */ + if ( eqr->eqr_entries == eqr->eqr_alloced ) { + eqr->eqr_alloced = MAX(100, eqr->eqr_alloced * 2); + eqr->eqr_array = realloc(eqr->eqr_array, + eqr->eqr_alloced * sizeof(epg_broadcast_t)); + } + + /* Store */ + eqr->eqr_array[eqr->eqr_entries++] = e; + // TODO: ref counting +} + +static void _eqr_add_channel + ( epg_query_result_t *eqr, epg_channel_t *ec ) +{ + epg_broadcast_t *ebc; + RB_FOREACH(ebc, &ec->ec_schedule, eb_slink) { + if ( ebc->eb_episode ) _eqr_add(eqr, ebc); + } +} + void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, const char *contentgroup, const char *title) { + // TODO: will need some real code here epg_channel_t *ec; - epg_broadcast_t *ebc; - eqr->eqr_array = calloc(2, sizeof(epg_broadcast_t*)); + + /* Clear (just incase) */ + memset(eqr, 0, sizeof(epg_query_result_t)); + RB_FOREACH(ec, &epg_channels, ec_link) { - RB_FOREACH(ebc, &ec->ec_schedule, eb_slink) { - if ( ebc->eb_episode ) { - eqr->eqr_array[0] = ebc; - eqr->eqr_entries = 1; - eqr->eqr_alloced = 2; - return; - } - } + _eqr_add_channel(eqr, ec); } return; } @@ -668,8 +698,16 @@ void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, void epg_query_free(epg_query_result_t *eqr) { free(eqr->eqr_array); + // TODO: reference counting +} + +static int _epg_sort_start_ascending ( const void *a, const void *b ) +{ + return (*(epg_broadcast_t**)a)->eb_start - (*(epg_broadcast_t**)b)->eb_start; } void epg_query_sort(epg_query_result_t *eqr) { + qsort(eqr->eqr_array, eqr->eqr_entries, sizeof(epg_broadcast_t*), + _epg_sort_start_ascending); } diff --git a/src/epg.h b/src/epg.h index f779dbbc..fe64852c 100644 --- a/src/epg.h +++ b/src/epg.h @@ -22,19 +22,6 @@ #include "channels.h" #include "settings.h" -/* ************************************************************************ - * Type definitions - * ***********************************************************************/ - -/* - * Forward declarations - */ -struct epg_brand; -struct epg_season; -struct epg_episode; -struct epg_broadcast; -struct epg_channel; - /* * Map types */ @@ -43,12 +30,22 @@ 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 - * e.g. The Simpsons, 24, Eastenders, etc... +/* + * Forward declerations */ +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_channel epg_channel_t; + +/* ************************************************************************ + * Brand - Represents a specific show + * e.g. The Simpsons, 24, Eastenders, etc... + * ***********************************************************************/ + +/* Object */ typedef struct epg_brand { RB_ENTRY(epg_brand) eb_link; ///< Global list link @@ -60,14 +57,34 @@ typedef struct epg_brand 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 } epg_brand_t; -/* - * Represents a season - */ +/* Lookup */ +epg_brand_t *epg_brand_find_by_uri ( const char *uri, int create ); + +/* Mutators */ +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)); +int epg_brand_set_season_count ( epg_brand_t *b, uint16_t season_count ) + __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 + * ***********************************************************************/ + +/* Object */ typedef struct epg_season { RB_ENTRY(epg_season) es_link; ///< Global list link @@ -85,9 +102,30 @@ typedef struct epg_season } epg_season_t; -/* - * Represents a particular episode - */ +/* Lookup */ +epg_season_t *epg_season_find_by_uri ( const char *uri, int create ); + +/* Mutators */ +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 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 + * + * TODO: ee_genre needs to be a list + * ***********************************************************************/ + +/* Object */ typedef struct epg_episode { RB_ENTRY(epg_episode) ee_link; ///< Global link @@ -100,7 +138,6 @@ typedef struct epg_episode 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 @@ -112,9 +149,41 @@ typedef struct epg_episode int ee_refcount; ///< Reference counting } epg_episode_t; -/* - * A specific broadcast of an episode - */ +/* Lookup */ +epg_episode_t *epg_episode_find_by_uri ( const char *uri, int create ); + +/* Mutators */ +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 ) + __attribute__((warn_unused_result)); +int epg_episode_set_summary ( epg_episode_t *e, const char *summary ) + __attribute__((warn_unused_result)); +int epg_episode_set_description ( epg_episode_t *e, const char *description ) + __attribute__((warn_unused_result)); +int epg_episode_set_number ( epg_episode_t *e, uint16_t number ) + __attribute__((warn_unused_result)); +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 u ) + __attribute__((warn_unused_result)); +int epg_episode_set_season ( epg_episode_t *e, epg_season_t *s, int u ) + __attribute__((warn_unused_result)); +int epg_episode_add_broadcast ( epg_episode_t *e, epg_broadcast_t *b, int u ) + __attribute__((warn_unused_result)); +int epg_episode_rem_broadcast ( epg_episode_t *e, epg_broadcast_t *b, int u ) + __attribute__((warn_unused_result)); + +/* Acessors */ +int epg_episode_get_number_onscreen ( epg_episode_t *e, char *b, int c ); + + +/* ************************************************************************ + * Broadcast - specific airing (channel & time) of an episode + * ***********************************************************************/ + +/* Object */ typedef struct epg_broadcast { RB_ENTRY(epg_broadcast) eb_slink; ///< Schedule link @@ -141,19 +210,29 @@ typedef struct epg_broadcast uint8_t eb_repeat; ///< Repeat screening epg_episode_t *eb_episode; ///< Episode shown - channel_t *eb_channel; ///< Channel being broadcast on + epg_channel_t *eb_channel; ///< Channel being broadcast on int eb_refcount; ///< Reference counting } epg_broadcast_t; -/* - * Channel for mappings - */ +/* Lookup */ +epg_broadcast_t *epg_broadcast_find_by_time ( epg_channel_t *ch, time_t start, time_t stop, int create ); + +/* Mutators */ +int epg_broadcast_set_episode ( epg_broadcast_t *b, epg_episode_t *e, int u ) + __attribute__((warn_unused_result)); + +/* ************************************************************************ + * Channel - provides mapping from EPG channels to real channels + * ***********************************************************************/ + +/* Object */ typedef struct epg_channel { RB_ENTRY(epg_channel) ec_link; ///< Global link char *ec_uri; ///< Channel URI + char *ec_name; ///< Channel name char **ec_sname; ///< DVB svc names (to map) int **ec_sid; ///< DVB svc ids (to map) @@ -163,6 +242,17 @@ typedef struct epg_channel } epg_channel_t; +/* Lookup */ +epg_channel_t *epg_channel_find_by_uri ( const char *uri, int create ); + +/* Mutators */ +int epg_channel_set_name ( epg_channel_t *c, const char *n ) + __attribute__((warn_unused_result)); + +/* ************************************************************************ + * Querying + * ***********************************************************************/ + /* * Query result */ @@ -172,6 +262,14 @@ typedef struct epg_query_result { int eqr_alloced; } epg_query_result_t; +void epg_query0(epg_query_result_t *eqr, channel_t *ch, channel_tag_t *ct, + uint8_t type, const char *title); +void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, + const char *contentgroup, const char *title); +void epg_query_free(epg_query_result_t *eqr); +void epg_query_sort(epg_query_result_t *eqr); + + /* ************************************************************************ * Function prototypes * ***********************************************************************/ @@ -183,93 +281,12 @@ typedef struct epg_query_result { void epg_init(void); void epg_save(void); -/** - * All the epg_X_set_ function return 1 if it actually changed - * the EPG records. otherwise it returns 0. - * - * If the caller detects that something has changed, it should call - * epg_event_updated(). - * - * There reason to put the burden on the caller is that the caller - * can combine multiple set()'s into one update - * - */ - -/* Brand set() calls */ -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)); -int epg_brand_set_season_count ( epg_brand_t *b, uint16_t season_count ) - __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_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 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_title ( epg_episode_t *e, const char *title ) - __attribute__((warn_unused_result)); -int epg_episode_set_subtitle ( epg_episode_t *e, const char *subtitle ) - __attribute__((warn_unused_result)); -int epg_episode_set_summary ( epg_episode_t *e, const char *summary ) - __attribute__((warn_unused_result)); -int epg_episode_set_description ( epg_episode_t *e, const char *description ) - __attribute__((warn_unused_result)); -int epg_episode_set_number ( epg_episode_t *e, uint16_t number ) - __attribute__((warn_unused_result)); -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 u ) - __attribute__((warn_unused_result)); -int epg_episode_set_season ( epg_episode_t *e, epg_season_t *s, int u ) - __attribute__((warn_unused_result)); -int epg_episode_add_broadcast ( epg_episode_t *e, epg_broadcast_t *b, int u ) - __attribute__((warn_unused_result)); -int epg_episode_rem_broadcast ( epg_episode_t *e, epg_broadcast_t *b, int u ) - __attribute__((warn_unused_result)); - - -/* 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 ); -void epg_brand_updated ( epg_brand_t *b ); -void epg_season_updated ( epg_season_t *s ); -void epg_episode_updated ( epg_episode_t *e ); -void epg_broadcast_updated ( epg_broadcast_t *b ); /* * Simple lookup */ -epg_brand_t *epg_brand_find_by_uri ( const char *uri, int create ); -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_by_time ( epg_channel_t *ch, time_t start, time_t stop, int create ); epg_broadcast_t *epg_event_find_by_id(int eventid); @@ -277,13 +294,6 @@ epg_broadcast_t *epg_event_find_by_id(int eventid); * Advanced Query */ -void epg_query0(epg_query_result_t *eqr, channel_t *ch, channel_tag_t *ct, - uint8_t type, const char *title); -void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, - const char *contentgroup, const char *title); -void epg_query_free(epg_query_result_t *eqr); -void epg_query_sort(epg_query_result_t *eqr); - /* * Genres? */ diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c index 2442fed5..5fc95e82 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -42,9 +42,14 @@ static int _pyepg_parse_channel ( htsmsg_t *data ) if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0; /* Find channel */ - if ((channel = epg_channel_find(id, name, NULL, NULL)) == NULL) return 0; + if ((channel = epg_channel_find_by_uri(id, 1)) == NULL) return 0; // TODO: need to save if created + /* Set name */ + name = htsmsg_xml_get_cdata_str(tags, "name"); + if ( name ) save |= epg_channel_set_name(channel, name); + + return save; } @@ -237,10 +242,7 @@ static int _pyepg_parse_broadcast ( htsmsg_t *data, epg_channel_t *channel ) if (!_pyepg_parse_time(stop, &tm_stop)) return 0; /* Find broadcast */ - 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 */ diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 03a30b02..d8e4ecec 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -676,6 +676,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) //epg_brand_t *eb = NULL; int start = 0, end, limit, i; const char *s; + char buf[100]; const char *channel = http_arg_get(&hc->hc_req_args, "channel"); const char *tag = http_arg_get(&hc->hc_req_args, "tag"); const char *cgrp = http_arg_get(&hc->hc_req_args, "contentgrp"); @@ -708,24 +709,21 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) end = MIN(start + limit, eqr.eqr_entries); for(i = start; i < end; i++) { - //const char *s; - //eb = NULL; es = NULL; ee = NULL; e = eqr.eqr_array[i]; ee = e->eb_episode; - //if (ee) es = ee->ee_season; - //if (ee) eb = ee->ee_brand; m = htsmsg_create_map(); -#if TODO - if(e->e_channel != NULL) { - htsmsg_add_str(m, "channel", e->e_channel->ch_name); + if(e->eb_channel != NULL) { + // TODO: this should probably be the real channel! + htsmsg_add_str(m, "channel", e->eb_channel->ec_name); +#if TODO_ADD_FULL_CHANNEL_INFO htsmsg_add_u32(m, "channelid", e->e_channel->ch_id); if(e->e_channel->ch_icon != NULL) htsmsg_add_str(m, "chicon", e->e_channel->ch_icon); - } #endif + } if(ee->ee_title != NULL) htsmsg_add_str(m, "title", ee->ee_title); @@ -735,12 +733,10 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) else if(ee->ee_summary != NULL) htsmsg_add_str(m, "description", ee->ee_summary); -#if TODO - if(e->e_episode.ee_onscreen != NULL) - htsmsg_add_str(m, "episode", e->e_episode.ee_onscreen); -#endif + if (epg_episode_get_number_onscreen(ee, buf, 100)) + htsmsg_add_str(m, "episode", strdup(buf)); -#if TODO +#if TODO_REMOVE_THIS_QQ if(e->e_ext_desc != NULL) htsmsg_add_str(m, "ext_desc", e->e_ext_desc); @@ -751,15 +747,19 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque) htsmsg_add_str(m, "ext_text", e->e_ext_text); #endif - //htsmsg_add_u32(m, "id", e->e_id); +#if TODO_ARE_WE_JUNKING_THIS + htsmsg_add_u32(m, "id", e->e_id); +#endif htsmsg_add_u32(m, "start", e->eb_start); htsmsg_add_u32(m, "end", e->eb_stop); htsmsg_add_u32(m, "duration", e->eb_stop - e->eb_start); -#if TODO +#if TODO_INCLUDE_GENRE_SUPORT if((s = epg_content_group_get_name(e->e_content_type)) != NULL) htsmsg_add_str(m, "contentgrp", s); +#endif +#if TODO_UPDATE_DVR_CODE dvr_entry_t *de; if((de = dvr_entry_find_by_event(e)) != NULL) htsmsg_add_str(m, "schedstate", dvr_entry_schedstatus(de));