diff --git a/src/epg.c b/src/epg.c index 2120d7bb..19f2e671 100644 --- a/src/epg.c +++ b/src/epg.c @@ -33,17 +33,28 @@ #include "dvr/dvr.h" #include "htsp.h" #include "htsmsg_binary.h" +#include "epggrab.h" -struct epg_brand_tree epg_brands; -struct epg_season_tree epg_seasons; -struct epg_episode_tree epg_episodes; -struct epg_channel_tree epg_channels; +/* Element lists */ +struct epg_brand_tree epg_brands; +struct epg_season_tree epg_seasons; +struct epg_episode_tree epg_episodes; +struct epg_channel_tree epg_channels; +struct epg_broadcast_tree epg_broadcasts; // TODO: do we need this +/* Unlinked channels */ LIST_HEAD(epg_unlinked_channel_list1, epg_channel); LIST_HEAD(epg_unlinked_channel_list2, channel); struct epg_unlinked_channel_list1 epg_unlinked_channels1; struct epg_unlinked_channel_list2 epg_unlinked_channels2; +/* Global counters */ +static uint32_t _epg_channel_idx = 0; +static uint32_t _epg_brand_idx = 0; +static uint32_t _epg_season_idx = 0; +static uint32_t _epg_episode_idx = 0; +static uint32_t _epg_broadcast_idx = 0; + /* ************************************************************************** * Comparators * *************************************************************************/ @@ -170,6 +181,13 @@ 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); +} + void epg_save ( void ) { int fd; @@ -183,18 +201,25 @@ void epg_save ( void ) // TODO: requires markers in the file or some other means of // determining where the various object types are? fd = hts_settings_open_file(1, "epgdb"); + + /* Channels */ + if ( _epg_write_sect(fd, "channels") ) return; RB_FOREACH(ec, &epg_channels, ec_link) { if (_epg_write(fd, epg_channel_serialize(ec))) return; } + if ( _epg_write_sect(fd, "brands") ) return; RB_FOREACH(eb, &epg_brands, eb_link) { if (_epg_write(fd, epg_brand_serialize(eb))) return; } + if ( _epg_write_sect(fd, "seasons") ) return; RB_FOREACH(es, &epg_seasons, es_link) { if (_epg_write(fd, epg_season_serialize(es))) return; } + if ( _epg_write_sect(fd, "episodes") ) return; RB_FOREACH(ee, &epg_episodes, ee_link) { if (_epg_write(fd, epg_episode_serialize(ee))) return; } + if ( _epg_write_sect(fd, "broadcasts") ) return; RB_FOREACH(ec, &epg_channels, ec_link) { RB_FOREACH(ebc, &ec->ec_schedule, eb_slink) { if (_epg_write(fd, epg_broadcast_serialize(ebc))) return; @@ -204,26 +229,102 @@ void epg_save ( void ) void epg_init ( void ) { + int save, fd; struct stat st; - int fd = hts_settings_open_file(0, "epgdb"); size_t remain; uint8_t *mem, *rp; - fstat(fd, &st); + char *sect = NULL; + const char *s; + epggrab_stats_t stats; + + /* Map file to memory */ + fd = hts_settings_open_file(0, "epgdb"); + if ( fd < 0 ) { + tvhlog(LOG_DEBUG, "epg", "database does not exist"); + return; + } + if ( fstat(fd, &st) != 0 ) { + tvhlog(LOG_ERR, "epg", "failed to detect database size"); + return; + } + if ( !st.st_size ) { + tvhlog(LOG_DEBUG, "epg", "database is empty"); + return; + } + remain = st.st_size; rp = mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - //TODO:if ( mem == MAP_FAILED ) return; - remain = st.st_size; + if ( mem == MAP_FAILED ) { + tvhlog(LOG_ERR, "epg", "failed to mmap database"); + return; + } + + /* Process */ + memset(&stats, 0, sizeof(stats)); while ( remain > 4 ) { + + // TODO: would be nice if htsmsg_binary handled this for us! + + /* Get message length */ int msglen = (rp[0] << 24) | (rp[1] << 16) | (rp[2] << 8) | rp[3]; - remain -= 4; - rp += 4; + remain -= 4; + rp += 4; + + /* Extract message */ htsmsg_t *m = htsmsg_binary_deserialize(rp, msglen, NULL); + + /* Process */ if(m) { - //htsmsg_print(m); + + /* New section */ + s = htsmsg_get_str(m, "__section__"); + if (s) { + if (sect) free(sect); + sect = strdup(s); + + /* Channel */ + } else if ( !strcmp(sect, "channels") ) { + if (epg_channel_deserialize(m, 1, &save)) stats.channels.total++; + + /* Brand */ + } else if ( !strcmp(sect, "brands") ) { + if (epg_brand_deserialize(m, 1, &save)) stats.brands.total++; + + /* Season */ + } else if ( !strcmp(sect, "seasons") ) { + if (epg_season_deserialize(m, 1, &save)) stats.seasons.total++; + + /* Episode */ + } else if ( !strcmp(sect, "episodes") ) { + if (epg_episode_deserialize(m, 1, &save)) stats.episodes.total++; + + /* Broadcasts */ + } else if ( !strcmp(sect, "broadcasts") ) { + if (epg_broadcast_deserialize(m, 1, &save)) stats.broadcasts.total++; + + /* Unknown */ + } else { + tvhlog(LOG_DEBUG, "epg", "malformed database section [%s]", sect); + //htsmsg_print(m); + } + + /* Cleanup */ htsmsg_destroy(m); } - rp += msglen; + + /* Next */ + rp += msglen; remain -= msglen; } + + /* Stats */ + tvhlog(LOG_DEBUG, "epg", "database loaded"); + tvhlog(LOG_DEBUG, "epg", "channels %d", stats.channels.total); + tvhlog(LOG_DEBUG, "epg", "brands %d", stats.brands.total); + 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); + + /* Close file */ munmap(mem, st.st_size); close(fd); } @@ -267,6 +368,7 @@ epg_brand_t* epg_brand_find_by_uri if ( skel == NULL ) skel = calloc(1, sizeof(epg_brand_t)); skel->eb_uri = (char*)id; + skel->eb_id = _epg_brand_idx; /* Find */ if ( !create ) { @@ -276,16 +378,26 @@ epg_brand_t* epg_brand_find_by_uri } else { eb = RB_INSERT_SORTED(&epg_brands, skel, eb_link, eb_uri_cmp); if ( eb == NULL ) { - eb = skel; - skel = NULL; + *save |= 1; + eb = skel; + skel = NULL; eb->eb_uri = strdup(id); - *save |= 1; + _epg_brand_idx++; } } return eb; } +epg_brand_t *epg_brand_find_by_id ( uint32_t id ) +{ + epg_brand_t *eb; + RB_FOREACH(eb, &epg_brands, eb_link) { + if ( eb->eb_id == id ) return eb; + } + return NULL; +} + int epg_brand_set_title ( epg_brand_t *brand, const char *title ) { int save = 0; @@ -383,11 +495,9 @@ int epg_brand_rem_episode ( epg_brand_t *brand, epg_episode_t *episode, int u ) htsmsg_t *epg_brand_serialize ( epg_brand_t *brand ) { htsmsg_t *m; - if ( !brand ) return NULL; + if ( !brand || !brand->eb_uri ) return NULL; m = htsmsg_create_map(); - // TODO: ID - if (brand->eb_uri) - htsmsg_add_str(m, "uri", brand->eb_uri); + htsmsg_add_str(m, "uri", brand->eb_uri); if (brand->eb_title) htsmsg_add_str(m, "title", brand->eb_title); if (brand->eb_summary) @@ -397,6 +507,25 @@ htsmsg_t *epg_brand_serialize ( epg_brand_t *brand ) return m; } +epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create, int *save ) +{ + epg_brand_t *eb; + uint32_t u32; + const char *str; + + if ( !(str = htsmsg_get_str(m, "uri")) ) return NULL; + if ( !(eb = epg_brand_find_by_uri(str, create, save)) ) return NULL; + + if ( (str = htsmsg_get_str(m, "title")) ) + *save |= epg_brand_set_title(eb, str); + if ( (str = htsmsg_get_str(m, "summary")) ) + *save |= epg_brand_set_summary(eb, str); + if ( !htsmsg_get_u32(m, "season-count", &u32) ) + *save |= epg_brand_set_season_count(eb, u32); + + return eb; +} + /* ************************************************************************** * Season * *************************************************************************/ @@ -411,6 +540,7 @@ epg_season_t* epg_season_find_by_uri if ( skel == NULL ) skel = calloc(1, sizeof(epg_season_t)); skel->es_uri = (char*)id; + skel->es_id = _epg_season_idx; /* Find */ if ( !create ) { @@ -420,16 +550,26 @@ epg_season_t* epg_season_find_by_uri } else { es = RB_INSERT_SORTED(&epg_seasons, skel, es_link, es_uri_cmp); if ( es == NULL ) { - es = skel; - skel = NULL; + *save |= 1; + es = skel; + skel = NULL; es->es_uri = strdup(id); - *save |= 1; + _epg_season_idx++; } } return es; } +epg_season_t *epg_season_find_by_id ( uint32_t id ) +{ + epg_season_t *es; + RB_FOREACH(es, &epg_seasons, es_link) { + if ( es->es_id == id ) return es; + } + return NULL; +} + int epg_season_set_summary ( epg_season_t *season, const char *summary ) { int save = 0; @@ -512,10 +652,9 @@ int epg_season_rem_episode htsmsg_t *epg_season_serialize ( epg_season_t *season ) { htsmsg_t *m; - if (!season) return NULL; + if (!season || !season->es_uri) return NULL; m = htsmsg_create_map(); - if (season->es_uri) - htsmsg_add_str(m, "uri", season->es_uri); + htsmsg_add_str(m, "uri", season->es_uri); if (season->es_summary) htsmsg_add_str(m, "summary", season->es_summary); if (season->es_number) @@ -523,11 +662,34 @@ htsmsg_t *epg_season_serialize ( epg_season_t *season ) if (season->es_episode_count) htsmsg_add_u32(m, "episode-count", season->es_episode_count); if (season->es_brand) - htsmsg_add_str(m, "brand-id", season->es_brand->eb_uri); - // TODO: change to ID + htsmsg_add_str(m, "brand", season->es_brand->eb_uri); return m; } +epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save ) +{ + epg_season_t *es; + epg_brand_t *eb; + uint32_t u32; + const char *str; + + if ( !(str = htsmsg_get_str(m, "uri")) ) return NULL; + if ( !(es = epg_season_find_by_uri(str, create, save)) ) return NULL; + + if ( (str = htsmsg_get_str(m, "summary")) ) + *save |= epg_season_set_summary(es, str); + if ( !htsmsg_get_u32(m, "number", &u32) ) + *save |= epg_season_set_number(es, u32); + if ( !htsmsg_get_u32(m, "episode-count", &u32) ) + *save |= epg_season_set_episode_count(es, u32); + + if ( (str = htsmsg_get_str(m, "brand")) ) + if ( (eb = epg_brand_find_by_uri(str, 0, NULL)) ) + *save |= epg_season_set_brand(es, eb, 1); + + return es; +} + /* ************************************************************************** * Episode * *************************************************************************/ @@ -542,6 +704,7 @@ epg_episode_t* epg_episode_find_by_uri if ( skel == NULL ) skel = calloc(1, sizeof(epg_episode_t)); skel->ee_uri = (char*)id; + skel->ee_id = _epg_episode_idx; /* Find */ if ( !create ) { @@ -551,16 +714,26 @@ epg_episode_t* epg_episode_find_by_uri } else { ee = RB_INSERT_SORTED(&epg_episodes, skel, ee_link, ee_uri_cmp); if ( ee == NULL ) { - ee = skel; - skel = NULL; + *save |= 1; + ee = skel; + skel = NULL; ee->ee_uri = strdup(id); - *save |= 1; + _epg_episode_idx++; } } return ee; } +epg_episode_t *epg_episode_find_by_id ( uint32_t id ) +{ + epg_episode_t *ee; + RB_FOREACH(ee, &epg_episodes, ee_link) { + if ( ee->ee_id == id ) return ee; + } + return NULL; +} + int epg_episode_set_title ( epg_episode_t *episode, const char *title ) { int save = 0; @@ -713,10 +886,9 @@ int epg_episode_get_number_onscreen htsmsg_t *epg_episode_serialize ( epg_episode_t *episode ) { htsmsg_t *m; - if (!episode) return NULL; + if (!episode || !episode->ee_uri) return NULL; m = htsmsg_create_map(); - if (episode->ee_uri) - htsmsg_add_str(m, "uri", episode->ee_uri); + htsmsg_add_str(m, "uri", episode->ee_uri); if (episode->ee_title) htsmsg_add_str(m, "title", episode->ee_title); if (episode->ee_subtitle) @@ -731,21 +903,52 @@ htsmsg_t *epg_episode_serialize ( epg_episode_t *episode ) htsmsg_add_u32(m, "part-number", episode->ee_part_number); htsmsg_add_u32(m, "part-count", episode->ee_part_count); } - // TODO: use ids if (episode->ee_brand) - htsmsg_add_str(m, "brand-id", episode->ee_brand->eb_uri); + htsmsg_add_str(m, "brand", episode->ee_brand->eb_uri); if (episode->ee_season) - htsmsg_add_str(m, "season-id", episode->ee_season->es_uri); + htsmsg_add_str(m, "season", episode->ee_season->es_uri); return m; } +epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save ) +{ + 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 ( (str = htsmsg_get_str(m, "title")) ) + *save |= epg_episode_set_title(ee, str); + if ( (str = htsmsg_get_str(m, "subtitle")) ) + *save |= epg_episode_set_subtitle(ee, str); + if ( (str = htsmsg_get_str(m, "summary")) ) + *save |= epg_episode_set_summary(ee, str); + if ( (str = htsmsg_get_str(m, "description")) ) + *save |= epg_episode_set_description(ee, str); + if ( !htsmsg_get_u32(m, "number", &u32) ) + *save |= epg_episode_set_number(ee, u32); + if ( !htsmsg_get_u32(m, "part-number", &u32) && + !htsmsg_get_u32(m, "part-count", &u32a) ) + *save |= epg_episode_set_part(ee, u32, u32a); + + if ( (str = htsmsg_get_str(m, "brand")) ) + if ( (eb = epg_brand_find_by_uri(str, 0, NULL)) ) + *save |= epg_episode_set_brand(ee, eb, 1); + if ( (str = htsmsg_get_str(m, "season")) ) + if ( (es = epg_season_find_by_uri(str, 0, NULL)) ) + *save |= epg_episode_set_season(ee, es, 1); + + return ee; +} + /* ************************************************************************** * Broadcast * *************************************************************************/ -static int _epg_broadcast_idx = 0; - -// Note: will find broadcast playing at this time (not necessarily // one that starts at this time) // // Note: do we need to pass in stop? @@ -774,9 +977,9 @@ epg_broadcast_t* epg_broadcast_find_by_time } else { eb = RB_INSERT_SORTED(&channel->ec_schedule, skel, eb_slink, ebc_win_cmp); if ( eb == NULL ) { - eb = skel; - skel = NULL; *save |= 1; + eb = skel; + skel = NULL; _epg_broadcast_idx++; } } @@ -784,9 +987,9 @@ epg_broadcast_t* epg_broadcast_find_by_time return eb; } -epg_broadcast_t *epg_broadcast_find_by_id ( int id ) +// TODO: optional channel? +epg_broadcast_t *epg_broadcast_find_by_id ( uint32_t id ) { - // TODO: do this properly epg_channel_t *ec; epg_broadcast_t *ebc; RB_FOREACH(ec, &epg_channels, ec_link) { @@ -820,19 +1023,59 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast ) { htsmsg_t *m; if (!broadcast) return NULL; + if (!broadcast->eb_channel || !broadcast->eb_channel->ec_uri) return NULL; + if (!broadcast->eb_episode || !broadcast->eb_episode->ee_uri) return NULL; m = htsmsg_create_map(); + htsmsg_add_u32(m, "id", broadcast->eb_id); htsmsg_add_u32(m, "start", broadcast->eb_start); htsmsg_add_u32(m, "stop", broadcast->eb_stop); + // TODO: should these be optional? + htsmsg_add_str(m, "channel", broadcast->eb_channel->ec_uri); + htsmsg_add_str(m, "episode", broadcast->eb_episode->ee_uri); + if (broadcast->eb_dvb_id) htsmsg_add_u32(m, "dvb-id", broadcast->eb_dvb_id); // TODO: add other metadata fields - // TODO: use ID - if (broadcast->eb_episode) - htsmsg_add_str(m, "episode-id", broadcast->eb_episode->ee_uri); return m; } +epg_broadcast_t *epg_broadcast_deserialize + ( htsmsg_t *m, int create, int *save ) +{ + epg_broadcast_t *ebc; + epg_channel_t *ec; + epg_episode_t *ee; + const char *str; + uint32_t id, start, stop; + + if ( htsmsg_get_u32(m, "id", &id) ) return NULL; + if ( htsmsg_get_u32(m, "start", &start) ) return NULL; + if ( htsmsg_get_u32(m, "stop", &stop) ) return NULL; + // TODO: should these be optional? + if ( !(str = htsmsg_get_str(m, "channel")) ) return NULL; + if ( !(ec = epg_channel_find_by_uri(str, 0, NULL)) ) 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(ec, start, stop, create, save); + if ( !ebc ) return NULL; + + *save |= epg_broadcast_set_episode(ebc, ee, 1); + + /* Bodge the ID - keep them the same */ + ebc->eb_id = id; + if ( id >= _epg_broadcast_idx ) _epg_broadcast_idx = id + 1; + +#if TODO_BROADCAST_METADATA + if ( !htsmsg_get_u32(m, "dvb-id", &u32) ) + save |= epg_broadcast_set_dvb_id(ebc, u32); + // TODO: more metadata +#endif + + return ebc; +} + /* ************************************************************************** * Channel * *************************************************************************/ @@ -855,6 +1098,7 @@ epg_channel_t* epg_channel_find_by_uri if ( skel == NULL ) skel = calloc(1, sizeof(epg_channel_t)); skel->ec_uri = (char*)id; + skel->ec_id = _epg_channel_idx; /* Find */ if ( !create ) { @@ -864,17 +1108,27 @@ epg_channel_t* epg_channel_find_by_uri } else { ec = RB_INSERT_SORTED(&epg_channels, skel, ec_link, ec_uri_cmp); if ( ec == NULL ) { - ec = skel; - skel = NULL; + *save |= 1; + ec = skel; + skel = NULL; ec->ec_uri = strdup(id); LIST_INSERT_HEAD(&epg_unlinked_channels1, ec, ec_ulink); - *save |= 1; + _epg_channel_idx++; } } return ec; } +epg_channel_t *epg_channel_find_by_id ( uint32_t id ) +{ + epg_channel_t *ec; + RB_FOREACH(ec, &epg_channels, ec_link) { + if (ec->ec_id == id) return ec; + } + return NULL; +} + int epg_channel_set_name ( epg_channel_t *channel, const char *name ) { int save = 0; @@ -898,16 +1152,35 @@ epg_broadcast_t *epg_channel_get_current_broadcast ( epg_channel_t *channel ) htsmsg_t *epg_channel_serialize ( epg_channel_t *channel ) { htsmsg_t *m; - if (!channel) return NULL; + if (!channel || !channel->ec_uri) return NULL; m = htsmsg_create_map(); - if (channel->ec_uri) - htsmsg_add_str(m, "uri", channel->ec_uri); + htsmsg_add_str(m, "uri", channel->ec_uri); if (channel->ec_channel) - htsmsg_add_u32(m, "channel-id", channel->ec_channel->ch_id); + htsmsg_add_u32(m, "channel", channel->ec_channel->ch_id); // TODO: other data return m; } +epg_channel_t *epg_channel_deserialize ( htsmsg_t *m, int create, int *save ) +{ + epg_channel_t *ec; +#if TODO_CHANNEL_LINK + channel_t *ch; + uint32_t u32; +#endif + const char *str; + + if ( !(str = htsmsg_get_str(m, "uri")) ) return NULL; + if ( !(ec = epg_channel_find_by_uri(str, create, save)) ) return NULL; + +#if TODO_CHANNEL_LINK + if ( !htsmsg_get_u32(m, "channel", &u32) ) + if ( (ch = channel_find_by_identifier(u32)) ) +#endif + + return ec; +} + /* ************************************************************************** * Querying * *************************************************************************/ diff --git a/src/epg.h b/src/epg.h index b642a17a..1dbbc071 100644 --- a/src/epg.h +++ b/src/epg.h @@ -16,6 +16,13 @@ * along with this program. If not, see . */ +/* + * TODO LIST: + * + * URI in the objects limits us to single grabber, might want something + * more flexible to try and merge grabbers? Is that feasible? + */ + #ifndef EPG_H #define EPG_H @@ -50,6 +57,7 @@ typedef struct epg_brand { RB_ENTRY(epg_brand) eb_link; ///< Global list link + uint32_t eb_id; ///< Internal ID char *eb_uri; ///< Grabber URI char *eb_title; ///< Brand name char *eb_summary; ///< Brand summary @@ -64,6 +72,7 @@ typedef struct epg_brand /* Lookup */ epg_brand_t *epg_brand_find_by_uri ( const char *uri, int create, int *save ); +epg_brand_t *epg_brand_find_by_id ( uint32_t id ); /* Mutators */ int epg_brand_set_title ( epg_brand_t *b, const char *title ) @@ -83,7 +92,7 @@ int epg_brand_rem_episode ( epg_brand_t *b, epg_episode_t *s, int u ) /* Serialization */ htsmsg_t *epg_brand_serialize ( epg_brand_t *b ); -epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create ); +epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create, int *save ); /* ************************************************************************ * Season @@ -95,6 +104,7 @@ typedef struct epg_season RB_ENTRY(epg_season) es_link; ///< Global list link RB_ENTRY(epg_season) es_blink; ///< Brand list link + uint32_t es_id; ///< Internal ID char *es_uri; ///< Grabber URI char *es_summary; ///< Season summary uint16_t es_number; ///< The season number @@ -110,6 +120,7 @@ typedef struct epg_season /* Lookup */ epg_season_t *epg_season_find_by_uri ( const char *uri, int create, int *save ); +epg_season_t *epg_season_find_by_id ( uint32_t id ); /* Mutators */ int epg_season_set_summary ( epg_season_t *s, const char *summary ) @@ -127,7 +138,7 @@ int epg_season_rem_episode ( epg_season_t *s, epg_episode_t *e, int u ) /* Serialization */ htsmsg_t *epg_season_serialize ( epg_season_t *b ); -epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create ); +epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save ); /* ************************************************************************ * Episode @@ -142,6 +153,7 @@ typedef struct epg_episode RB_ENTRY(epg_episode) ee_blink; ///< Brand link RB_ENTRY(epg_episode) ee_slink; ///< Season link + uint32_t ee_id; ///< Internal ID char *ee_uri; ///< Grabber URI char *ee_title; ///< Title char *ee_subtitle; ///< Sub-title @@ -162,6 +174,7 @@ typedef struct epg_episode /* Lookup */ epg_episode_t *epg_episode_find_by_uri ( const char *uri, int create, int *save ); +epg_episode_t *epg_episode_find_by_id ( uint32_t id ); /* Mutators */ int epg_episode_set_title ( epg_episode_t *e, const char *title ) @@ -191,7 +204,7 @@ int epg_episode_get_number_onscreen ( epg_episode_t *e, char *b, int c ); /* Serialization */ htsmsg_t *epg_episode_serialize ( epg_episode_t *b ); -epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create ); +epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save ); /* ************************************************************************ * Broadcast - specific airing (channel & time) of an episode @@ -203,8 +216,8 @@ typedef struct epg_broadcast 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 + uint32_t eb_id; ///< Internal ID + uint32_t eb_dvb_id; ///< DVB identifier time_t eb_start; ///< Start time time_t eb_stop; ///< End time @@ -232,7 +245,7 @@ typedef struct epg_broadcast /* Lookup */ epg_broadcast_t *epg_broadcast_find_by_time ( epg_channel_t *ch, time_t start, time_t stop, int create, int *save ); -epg_broadcast_t *epg_broadcast_find_by_id ( int id ); +epg_broadcast_t *epg_broadcast_find_by_id ( uint32_t id ); /* Mutators */ int epg_broadcast_set_episode ( epg_broadcast_t *b, epg_episode_t *e, int u ) @@ -243,7 +256,8 @@ epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *b ); /* Serialization */ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *b ); -epg_broadcast_t *epg_broadcast_deserialize ( htsmsg_t *m, int create ); +epg_broadcast_t *epg_broadcast_deserialize + ( htsmsg_t *m, int create, int *save ); /* ************************************************************************ * Channel - provides mapping from EPG channels to real channels @@ -254,7 +268,8 @@ typedef struct epg_channel { RB_ENTRY(epg_channel) ec_link; ///< Global link - char *ec_uri; ///< Channel URI + uint32_t ec_id; ///< Internal ID + char *ec_uri; ///< Grabber URI char *ec_name; ///< Channel name char **ec_sname; ///< DVB svc names (to map) int **ec_sid; ///< DVB svc ids (to map) @@ -269,6 +284,7 @@ typedef struct epg_channel /* Lookup */ epg_channel_t *epg_channel_find_by_uri ( const char *uri, int create, int *save ); +epg_channel_t *epg_channel_find_by_id ( uint32_t id ); /* Mutators */ int epg_channel_set_name ( epg_channel_t *c, const char *n ) @@ -279,7 +295,7 @@ epg_broadcast_t *epg_channel_get_current_broadcast ( epg_channel_t *c ); /* Serialization */ htsmsg_t *epg_channel_serialize ( epg_channel_t *b ); -epg_channel_t *epg_channel_deserialize ( htsmsg_t *m, int create ); +epg_channel_t *epg_channel_deserialize ( htsmsg_t *m, int create, int *save ); /* ************************************************************************ * Querying