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;