diff --git a/src/epg.c b/src/epg.c index 127054d0..64e49a10 100644 --- a/src/epg.c +++ b/src/epg.c @@ -33,3 +33,233 @@ #include "htsp.h" #include "htsmsg_binary.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; + +void epg_init ( void ) +{ +} + +void epg_save ( void ) +{ +} + +void epg_updated ( void ) +{ +} + +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); +} + +epg_brand_t* epg_brand_find_by_uri ( const char *id, int create ) +{ + epg_brand_t *eb; + static epg_brand_t* skel = NULL; + + lock_assert(&global_lock); // pointless! + + if ( skel == NULL ) skel = calloc(1, sizeof(epg_brand_t)); + + /* 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); + + /* Create */ + if ( !eb && create ) { + 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 ) +{ + return strcmp(a->es_uri, b->es_uri); +} + +epg_season_t* epg_season_find_by_uri ( const char *id, int create ) +{ + epg_season_t *es; + static epg_season_t* skel = NULL; + + lock_assert(&global_lock); // pointless! + + if ( skel == NULL ) skel = calloc(1, sizeof(epg_season_t)); + + /* 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); + + /* Create */ + if ( !es && create ) { + 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 ) +{ + return strcmp(a->ee_uri, b->ee_uri); +} + +epg_episode_t* epg_episode_find_by_uri ( const char *id, int create ) +{ + epg_episode_t *ee; + static epg_episode_t* skel = NULL; + + lock_assert(&global_lock); // pointless! + + if ( skel == NULL ) skel = calloc(1, sizeof(epg_episode_t)); + + /* 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); + + /* Create */ + if ( !ee && create ) { + 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 ) +{ + return strcmp(a->ec_uri, b->ec_uri); +} + +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)); + + /* 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); + + /* Create */ + if ( !ec && create ) { + 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* channel; + + /* Find or create */ + if ((channel = epg_channel_find_by_uri(id, 1)) == NULL) return NULL; + + /* Update fields? */ + + return channel; +} diff --git a/src/epg.h b/src/epg.h index 69b1a9dc..2926bb96 100644 --- a/src/epg.h +++ b/src/epg.h @@ -34,6 +34,10 @@ 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); /* * Represents a specific show @@ -41,6 +45,7 @@ struct 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 @@ -60,6 +65,7 @@ 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 @@ -78,6 +84,7 @@ 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 @@ -129,6 +136,13 @@ 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? } epg_channel_t; /* @@ -182,7 +196,7 @@ 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, const epg_brand_t *b ) +int epg_season_set_brand ( epg_season_t *s, epg_brand_t *b ) __attribute__((warn_unused_result)); /* Episode set() calls */ @@ -221,11 +235,11 @@ void epg_broadcast_updated ( epg_broadcast_t *b ); * Simple lookup */ -epg_brand_t *epg_brand_find_by_id ( const char *id, int create ); -epg_season_t *epg_season_find_by_id ( const char *id, int create ); -epg_episode_t *epg_episode_find_by_id ( const char *id, int create ); -epg_channel_t *epg_channel_find_by_id ( const char *id, int create ); -epg_channel_t *epg_channel_find ( const char *id, const char *name, const char **sname, const int **sid ); +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 ( 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); diff --git a/src/epggrab.c b/src/epggrab.c index 7a357988..b811291a 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -4,6 +4,7 @@ #include "htsmsg.h" #include "settings.h" #include "tvheadend.h" +#include "epg.h" #include "epggrab.h" #include "epggrab/pyepg.h" @@ -52,13 +53,39 @@ void epggrab_init ( void ) pthread_create(&tid, &tattr, _epggrab_thread, NULL); } +/* + * Grab from module + */ +static void _epggrab_module_run ( epggrab_module_t *mod, const char *opts ) +{ + int save = 0; + time_t tm1, tm2; + htsmsg_t *data; + + /* Check */ + if ( !mod ) return; + + /* Grab */ + time(&tm1); + data = mod->grab(opts); + time(&tm2); + if ( !data ) { + tvhlog(LOG_WARNING, mod->name(), "grab returned no data"); + } else { + tvhlog(LOG_DEBUG, mod->name(), "grab took %d seconds", tm2 - tm1); + pthread_mutex_lock(&global_lock); + save = mod->parse(data); + if (save) epg_updated(); + pthread_mutex_unlock(&global_lock); + htsmsg_destroy(data); + } +} + /* * Simple run */ time_t _epggrab_thread_simple ( void ) { - htsmsg_t *data; - /* Copy config */ time_t ret = time(NULL) + epggrab_interval; epggrab_module_t* mod = epggrab_module; @@ -67,15 +94,7 @@ time_t _epggrab_thread_simple ( void ) pthread_mutex_unlock(&epggrab_mutex); /* Run the module */ - if ( mod ) { - data = mod->grab(NULL); - if ( data ) { - pthread_mutex_lock(&global_lock); - mod->parse(data); - pthread_mutex_unlock(&global_lock); - htsmsg_destroy(data); - } - } + _epggrab_module_run(mod, NULL); /* Re-lock */ pthread_mutex_lock(&epggrab_mutex); @@ -91,21 +110,12 @@ time_t _epggrab_thread_simple ( void ) */ time_t _epggrab_thread_advanced ( void ) { - htsmsg_t *data; epggrab_sched_t *s; /* Determine which to run */ LIST_FOREACH(s, &epggrab_schedule, es_link) { if ( cron_is_time(&s->cron) ) { - if ( s->mod ) { - data = s->mod->grab(s->opts); - if ( data ) { - pthread_mutex_lock(&global_lock); - s->mod->parse(data); - pthread_mutex_unlock(&global_lock); - htsmsg_destroy(data); - } - } + _epggrab_module_run(s->mod, s->opts); } } diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c index 8988eea3..36fd0ed2 100644 --- a/src/epggrab/pyepg.c +++ b/src/epggrab/pyepg.c @@ -33,6 +33,7 @@ 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; @@ -54,9 +55,10 @@ 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_id(str, 1)) == NULL) return 0; + if ((brand = epg_brand_find_by_uri(str, 1)) == NULL) return 0; // TODO: do we need to save if created? /* Set title */ @@ -98,14 +100,17 @@ 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_id(str, 1)) == NULL) return 0; + 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_xml_get_cdata_str(tags, "brand"))) { - if ((brand = epg_brand_find_by_id(str, 0))) { + 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); } } @@ -155,21 +160,25 @@ 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_id(str, 1)) == NULL) return 0; + 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_xml_get_cdata_str(tags, "brand"))) { - if ((brand = epg_brand_find_by_id(str, 0))) { + 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); } } /* Set season */ - if ((str = htsmsg_xml_get_cdata_str(tags, "season"))) { - if ((season = epg_season_find_by_id(str, 0))) { + 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); } } @@ -221,9 +230,10 @@ 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_id(id, 1)) == NULL) return 0; + if ((episode = epg_episode_find_by_uri(id, 1)) == NULL) return 0; /* Parse times */ if (!_pyepg_parse_time(start, &tm_start)) return 0; @@ -251,11 +261,12 @@ 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; - if ((channel = epg_channel_find_by_id(str, 0)) == 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; HTSMSG_FOREACH(f, tags) { - if (strcmp(f->hmf_name, "broadcast")) { + if (strcmp(f->hmf_name, "broadcast") == 0) { save |= _pyepg_parse_broadcast(htsmsg_get_map_by_field(f), channel); } } @@ -268,6 +279,7 @@ 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; @@ -292,6 +304,7 @@ 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; @@ -326,8 +339,6 @@ static htsmsg_t* _pyepg_grab ( const char *iopts ) int i, outlen; char *outbuf; char errbuf[100]; - time_t t1, t2; - htsmsg_t *body; const char *argv[32]; // 32 args max! char *toksave, *tok; char *opts = NULL; @@ -349,24 +360,15 @@ static htsmsg_t* _pyepg_grab ( const char *iopts ) tvhlog(LOG_DEBUG, "pyepg", "grab %s %s", argv[0], iopts ? iopts : ""); /* Grab */ - time(&t1); outlen = spawn_and_store_stdout(argv[0], (char *const*)argv, &outbuf); free(opts); if ( outlen < 1 ) { tvhlog(LOG_ERR, "pyepg", "no output detected"); return NULL; } - time(&t2); - tvhlog(LOG_DEBUG, "pyepg", "grab took %d seconds", t2 - t1); /* Extract */ - body = htsmsg_xml_deserialize(outbuf, errbuf, sizeof(errbuf)); - if ( !body ) { - tvhlog(LOG_ERR, "pyepg", "unable to parse output [e=%s]", errbuf); - return NULL; - } - - return body; + return htsmsg_xml_deserialize(outbuf, errbuf, sizeof(errbuf)); } epggrab_module_t* pyepg_init ( void )