diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index 909f482f..0cfe6db7 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -345,6 +345,7 @@ void dvr_autorec_add_series_link(const char *dvr_config_name,
void dvr_autorec_check_event(epg_broadcast_t *e);
void dvr_autorec_check_brand(epg_brand_t *b);
void dvr_autorec_check_season(epg_season_t *s);
+void dvr_autorec_check_serieslink(epg_serieslink_t *s);
void autorec_destroy_by_channel(channel_t *ch);
diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c
index d439c273..e414da85 100644
--- a/src/dvr/dvr_autorec.c
+++ b/src/dvr/dvr_autorec.c
@@ -604,6 +604,11 @@ void dvr_autorec_check_season(epg_season_t *s)
// this will already have been picked up by the check_event call
}
+void dvr_autorec_check_serieslink(epg_serieslink_t *s)
+{
+// TODO: need to implement this
+}
+
/**
*
*/
diff --git a/src/epg.c b/src/epg.c
index 2c554b44..4814dc87 100644
--- a/src/epg.c
+++ b/src/epg.c
@@ -41,6 +41,7 @@
epg_object_tree_t epg_brands;
epg_object_tree_t epg_seasons;
epg_object_tree_t epg_episodes;
+epg_object_tree_t epg_serieslinks;
/* Other special case lists */
epg_object_list_t epg_objects[EPG_HASH_WIDTH];
@@ -299,6 +300,17 @@ static int _epg_object_set_lang_str
return save;
}
+static int _epg_object_set_lang_str2
+ ( void *o, lang_str_t **old, const lang_str_t *str, epggrab_module_t *src )
+{
+ int save = 0;
+ lang_str_ele_t *ls;
+ RB_FOREACH(ls, str, link) {
+ save |= _epg_object_set_lang_str(o, old, ls->str, ls->lang, src);
+ }
+ return save;
+}
+
static int _epg_object_set_u8
( void *o, uint8_t *old, const uint8_t new, epggrab_module_t *src )
{
@@ -781,10 +793,17 @@ int epg_episode_set_title
( epg_episode_t *episode, const char *title, const char *lang,
epggrab_module_t *src )
{
- if (!episode || !title || !*title) return 0;
+ if (!episode) return 0;
return _epg_object_set_lang_str(episode, &episode->title, title, lang, src);
}
+int epg_episode_set_title2
+ ( epg_episode_t *episode, const lang_str_t *str, epggrab_module_t *src )
+{
+ if (!episode) return 0;
+ return _epg_object_set_lang_str2(episode, &episode->title, str, src);
+}
+
int epg_episode_set_subtitle
( epg_episode_t *episode, const char *subtitle, const char *lang,
epggrab_module_t *src )
@@ -794,6 +813,13 @@ int epg_episode_set_subtitle
subtitle, lang, src);
}
+int epg_episode_set_subtitle2
+ ( epg_episode_t *episode, const lang_str_t *str, epggrab_module_t *src )
+{
+ if (!episode) return 0;
+ return _epg_object_set_lang_str2(episode, &episode->subtitle, str, src);
+}
+
int epg_episode_set_summary
( epg_episode_t *episode, const char *summary, const char *lang,
epggrab_module_t *src )
@@ -1141,6 +1167,89 @@ const char *epg_episode_get_description
return lang_str_get(e->description, lang);
}
+/* **************************************************************************
+ * Series link
+ * *************************************************************************/
+
+static void _epg_serieslink_destroy ( void *eo )
+{
+ epg_serieslink_t *es = (epg_serieslink_t*)eo;
+ if (LIST_FIRST(&es->broadcasts)) {
+ tvhlog(LOG_CRIT, "epg", "attempt to destory series link with broadcasts");
+ assert(0);
+ }
+ _epg_object_destroy(eo, &epg_serieslinks);
+ free(es);
+}
+
+static void _epg_serieslink_updated ( void *eo )
+{
+ dvr_autorec_check_serieslink((epg_serieslink_t*)eo);
+}
+
+static epg_object_t **_epg_serieslink_skel ( void )
+{
+ static epg_object_t *skel = NULL;
+ if ( !skel ) {
+ skel = calloc(1, sizeof(epg_serieslink_t));
+ skel->type = EPG_SERIESLINK;
+ skel->destroy = _epg_serieslink_destroy;
+ skel->updated = _epg_serieslink_updated;
+ }
+ return &skel;
+}
+
+epg_serieslink_t* epg_serieslink_find_by_uri
+ ( const char *uri, int create, int *save )
+{
+ return (epg_serieslink_t*)
+ _epg_object_find_by_uri(uri, create, save,
+ &epg_serieslinks,
+ _epg_serieslink_skel());
+}
+
+epg_serieslink_t *epg_serieslink_find_by_id ( uint64_t id )
+{
+ return (epg_serieslink_t*)_epg_object_find_by_id(id, EPG_SERIESLINK);
+}
+
+static void _epg_serieslink_add_broadcast
+ ( epg_serieslink_t *esl, epg_broadcast_t *ebc )
+{
+ _epg_object_getref(esl);
+ _epg_object_set_updated(esl);
+ LIST_INSERT_HEAD(&esl->broadcasts, ebc, sl_link);
+}
+
+static void _epg_serieslink_rem_broadcast
+ ( epg_serieslink_t *esl, epg_broadcast_t *ebc )
+{
+ LIST_REMOVE(ebc, sl_link);
+ _epg_object_set_updated(esl);
+ _epg_object_putref(esl);
+}
+
+htsmsg_t *epg_serieslink_serialize ( epg_serieslink_t *esl )
+{
+ htsmsg_t *m;
+ if (!esl || !esl->uri) return NULL;
+ if (!(m = _epg_object_serialize((epg_object_t*)esl))) return NULL;
+ return m;
+}
+
+epg_serieslink_t *epg_serieslink_deserialize
+ ( htsmsg_t *m, int create, int *save )
+{
+ epg_object_t **skel = _epg_serieslink_skel();
+ epg_serieslink_t *esl;
+
+ if ( !_epg_object_deserialize(m, *skel) ) return NULL;
+ if ( !(esl = epg_serieslink_find_by_uri((*skel)->uri, create, save)) )
+ return NULL;
+
+ return esl;
+}
+
/* **************************************************************************
* Channel
* *************************************************************************/
@@ -1371,6 +1480,21 @@ int epg_broadcast_set_episode
return save;
}
+int epg_broadcast_set_serieslink
+ ( epg_broadcast_t *ebc, epg_serieslink_t *esl, epggrab_module_t *src )
+{
+ int save = 0;
+ if ( !ebc || !esl ) return 0;
+ if ( !_epg_object_set_grabber(ebc, src) && ebc->serieslink ) return 0;
+ if ( ebc->serieslink != esl ) {
+ if ( ebc->serieslink ) _epg_serieslink_rem_broadcast(ebc->serieslink, ebc);
+ ebc->serieslink = esl;
+ _epg_serieslink_add_broadcast(esl, ebc);
+ save = 1;
+ }
+ return save;
+}
+
int epg_broadcast_set_is_widescreen
( epg_broadcast_t *b, uint8_t ws, epggrab_module_t *src )
{
@@ -1434,12 +1558,75 @@ int epg_broadcast_set_is_repeat
return _epg_object_set_u8(b, &b->is_repeat, r, src);
}
+int epg_broadcast_set_summary
+ ( epg_broadcast_t *b, const char *str, const char *lang,
+ epggrab_module_t *src )
+{
+ if (!b) return 0;
+ return _epg_object_set_lang_str(b, &b->summary, str, lang, src);
+}
+
+int epg_broadcast_set_description
+ ( epg_broadcast_t *b, const char *str, const char *lang,
+ epggrab_module_t *src )
+{
+ if (!b) return 0;
+ return _epg_object_set_lang_str(b, &b->description, str, lang, src);
+}
+
+int epg_broadcast_set_summary2
+ ( epg_broadcast_t *b, const lang_str_t *str, epggrab_module_t *src )
+{
+ if (!b) return 0;
+ return _epg_object_set_lang_str2(b, &b->summary, str, src);
+}
+
+int epg_broadcast_set_description2
+ ( epg_broadcast_t *b, const lang_str_t *str, epggrab_module_t *src )
+{
+ if (!b) return 0;
+ return _epg_object_set_lang_str2(b, &b->description, str, src);
+}
+
epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *broadcast )
{
if ( !broadcast ) return NULL;
return RB_NEXT(broadcast, sched_link);
}
+epg_episode_t *epg_broadcast_get_episode
+ ( epg_broadcast_t *ebc, int create, int *save )
+{
+ char uri[256];
+ epg_episode_t *ee;
+ if (!ebc) return NULL;
+ if (ebc->episode) return ebc->episode;
+ if (!create) return NULL;
+ snprintf(uri, sizeof(uri)-1, "tvh://channel-%d/bcast-%"PRIu64"/episode",
+ ebc->channel->ch_id, ebc->id);
+ if ((ee = epg_episode_find_by_uri(uri, 1, save)))
+ *save |= epg_broadcast_set_episode(ebc, ee, ebc->grabber);
+ return ee;
+}
+
+const char *epg_broadcast_get_title ( epg_broadcast_t *b, const char *lang )
+{
+ if (!b || !b->episode) return NULL;
+ return epg_episode_get_title(b->episode, lang);
+}
+
+const char *epg_broadcast_get_summary ( epg_broadcast_t *b, const char *lang )
+{
+ if (!b || !b->summary) return NULL;
+ return lang_str_get(b->summary, lang);
+}
+
+const char *epg_broadcast_get_description ( epg_broadcast_t *b, const char *lang )
+{
+ if (!b || !b->description) return NULL;
+ return lang_str_get(b->description, lang);
+}
+
htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
{
htsmsg_t *m;
@@ -1471,6 +1658,8 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
htsmsg_add_u32(m, "is_new", 1);
if (broadcast->is_repeat)
htsmsg_add_u32(m, "is_repeat", 1);
+ if (broadcast->serieslink)
+ htsmsg_add_str(m, "serieslink", broadcast->serieslink->uri);
return m;
}
@@ -1481,6 +1670,7 @@ epg_broadcast_t *epg_broadcast_deserialize
channel_t *ch = NULL;
epg_broadcast_t *ebc, **skel = _epg_broadcast_skel();
epg_episode_t *ee;
+ epg_serieslink_t *esl;
const char *str;
uint32_t chid, eid, start, stop, u32;
@@ -1532,6 +1722,11 @@ epg_broadcast_t *epg_broadcast_deserialize
if (!htsmsg_get_u32(m, "is_repeat", &u32))
*save |= epg_broadcast_set_is_repeat(ebc, u32, NULL);
+ /* Series link */
+ if ((str = htsmsg_get_str(m, "serieslink")))
+ if ((esl = epg_serieslink_find_by_uri(str, 1, save)))
+ *save |= epg_broadcast_set_serieslink(ebc, esl, NULL);
+
/* Set the episode */
*save |= epg_broadcast_set_episode(ebc, ee, NULL);
diff --git a/src/epg.h b/src/epg.h
index 336a0205..6f21c0e7 100644
--- a/src/epg.h
+++ b/src/epg.h
@@ -32,14 +32,14 @@ struct epggrab_module;
/*
* Map/List types
*/
-LIST_HEAD(epg_object_list, epg_object);
-RB_HEAD (epg_object_tree, epg_object);
-LIST_HEAD(epg_brand_list, epg_brand);
-LIST_HEAD(epg_season_list, epg_season);
-LIST_HEAD(epg_episode_list, epg_episode);
-LIST_HEAD(epg_broadcast_list, epg_broadcast);
-RB_HEAD (epg_broadcast_tree, epg_broadcast);
-LIST_HEAD(epg_genre_list, epg_genre);
+typedef LIST_HEAD(,epg_object) epg_object_list_t;
+typedef RB_HEAD (,epg_object) epg_object_tree_t;
+typedef LIST_HEAD(,epg_brand) epg_brand_list_t;
+typedef LIST_HEAD(,epg_season) epg_season_list_t;
+typedef LIST_HEAD(,epg_episode) epg_episode_list_t;
+typedef LIST_HEAD(,epg_broadcast) epg_broadcast_list_t;
+typedef RB_HEAD (,epg_broadcast) epg_broadcast_tree_t;
+typedef LIST_HEAD(,epg_genre) epg_genre_list_t;
/*
* Typedefs (most are redundant!)
@@ -50,13 +50,7 @@ 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_season_list epg_season_list_t;
-typedef struct epg_episode_list epg_episode_list_t;
-typedef struct epg_broadcast_list epg_broadcast_list_t;
-typedef struct epg_broadcast_tree epg_broadcast_tree_t;
-typedef struct epg_object_list epg_object_list_t;
-typedef struct epg_object_tree epg_object_tree_t;
-typedef struct epg_genre_list epg_genre_list_t;
+typedef struct epg_serieslink epg_serieslink_t;
/* ************************************************************************
* Genres
@@ -100,7 +94,8 @@ typedef enum epg_object_type
EPG_BRAND,
EPG_SEASON,
EPG_EPISODE,
- EPG_BROADCAST
+ EPG_BROADCAST,
+ EPG_SERIESLINK
} epg_object_type_t;
/* Object */
@@ -329,7 +324,13 @@ int epg_episode_set_image
( epg_episode_t *e, const char *i, struct epggrab_module *src )
__attribute__((warn_unused_result));
int epg_episode_set_is_bw
- ( epg_episode_t *b, uint8_t bw, struct epggrab_module *src )
+ ( epg_episode_t *e, uint8_t bw, struct epggrab_module *src )
+ __attribute__((warn_unused_result));
+int epg_episode_set_title2
+ ( epg_episode_t *e, const lang_str_t *str, struct epggrab_module *src )
+ __attribute__((warn_unused_result));
+int epg_episode_set_subtitle2
+ ( epg_episode_t *e, const lang_str_t *str, struct epggrab_module *src )
__attribute__((warn_unused_result));
// Note: this does NOT strdup the text field
@@ -362,6 +363,29 @@ int epg_episode_fuzzy_match
htsmsg_t *epg_episode_serialize ( epg_episode_t *b );
epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save );
+/* ************************************************************************
+ * Series Link - broadcast level linkage
+ * ***********************************************************************/
+
+/* Object */
+struct epg_serieslink
+{
+ epg_object_t;
+
+ epg_broadcast_list_t broadcasts; ///< Episode list
+};
+
+/* Lookup */
+epg_serieslink_t *epg_serieslink_find_by_uri
+ ( const char *uri, int create, int *save );
+epg_serieslink_t *epg_serieslink_find_by_id
+ ( uint64_t id );
+
+/* Serialization */
+htsmsg_t *epg_serieslink_serialize ( epg_serieslink_t *s );
+epg_serieslink_t *epg_serieslink_deserialize
+ ( htsmsg_t *m, int create, int *save );
+
/* ************************************************************************
* Broadcast - specific airing (channel & time) of an episode
* ***********************************************************************/
@@ -390,9 +414,15 @@ struct epg_broadcast
uint8_t is_new; ///< New series / file premiere
uint8_t is_repeat; ///< Repeat screening
+ /* Broadcast level text */
+ lang_str_t *summary; ///< Summary
+ lang_str_t *description; ///< Description
+
RB_ENTRY(epg_broadcast) sched_link; ///< Schedule link
LIST_ENTRY(epg_broadcast) ep_link; ///< Episode link
epg_episode_t *episode; ///< Episode shown
+ LIST_ENTRY(epg_broadcast) sl_link; ///< SeriesLink link
+ epg_serieslink_t *serieslink; ///< SeriesLink;
struct channel *channel; ///< Channel being broadcast on
};
@@ -435,9 +465,34 @@ int epg_broadcast_set_is_new
int epg_broadcast_set_is_repeat
( epg_broadcast_t *b, uint8_t r, struct epggrab_module *src )
__attribute__((warn_unused_result));
+int epg_broadcast_set_summary
+ ( epg_broadcast_t *b, const char *str, const char *lang,
+ struct epggrab_module *src )
+ __attribute__((warn_unused_result));
+int epg_broadcast_set_description
+ ( epg_broadcast_t *b, const char *str, const char *lang,
+ struct epggrab_module *src )
+ __attribute__((warn_unused_result));
+int epg_broadcast_set_summary2
+ ( epg_broadcast_t *b, const lang_str_t *str, struct epggrab_module *src )
+ __attribute__((warn_unused_result));
+int epg_broadcast_set_description2
+ ( epg_broadcast_t *b, const lang_str_t *str, struct epggrab_module *src )
+ __attribute__((warn_unused_result));
+int epg_broadcast_set_serieslink
+ ( epg_broadcast_t *b, epg_serieslink_t *sl, struct epggrab_module *src )
+ __attribute__((warn_unused_result));
/* Accessors */
-epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *b );
+epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *b );
+epg_episode_t *epg_broadcast_get_episode
+ ( epg_broadcast_t *b, int create, int *save );
+const char *epg_broadcast_get_title
+ ( epg_broadcast_t *b, const char *lang );
+const char *epg_broadcast_get_summary
+ ( epg_broadcast_t *b, const char *lang );
+const char *epg_broadcast_get_description
+ ( epg_broadcast_t *b, const char *lang );
/* Serialization */
htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *b );
diff --git a/src/epgdb.c b/src/epgdb.c
index 19433c0a..f56e5982 100644
--- a/src/epgdb.c
+++ b/src/epgdb.c
@@ -34,6 +34,7 @@
extern epg_object_tree_t epg_brands;
extern epg_object_tree_t epg_seasons;
extern epg_object_tree_t epg_episodes;
+extern epg_object_tree_t epg_serieslinks;
/* **************************************************************************
* Load
@@ -119,6 +120,10 @@ static void _epgdb_v2_process ( htsmsg_t *m, epggrab_stats_t *stats )
} else if ( !strcmp(sect, "episodes") ) {
if (epg_episode_deserialize(m, 1, &save)) stats->episodes.total++;
+ /* Series link */
+ } else if ( !strcmp(sect, "serieslinks") ) {
+ if (epg_serieslink_deserialize(m, 1, &save)) stats->seasons.total++;
+
/* Broadcasts */
} else if ( !strcmp(sect, "broadcasts") ) {
if (epg_broadcast_deserialize(m, 1, &save)) stats->broadcasts.total++;
@@ -284,6 +289,12 @@ void epg_save ( void )
if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) return;
stats.episodes.total++;
}
+ if ( _epg_write_sect(fd, "serieslinks") ) return;
+ RB_FOREACH(eo, &epg_serieslinks, uri_link) {
+ if (_epg_write(fd, epg_serieslink_serialize((epg_serieslink_t*)eo)))
+ return;
+ stats.seasons.total++;
+ }
if ( _epg_write_sect(fd, "broadcasts") ) return;
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c
index c435679c..c58124bc 100644
--- a/src/epggrab/module/eit.c
+++ b/src/epggrab/module/eit.c
@@ -410,9 +410,8 @@ static int _eit_process_event
uint8_t dtag, dlen;
epg_broadcast_t *ebc;
epg_episode_t *ee;
- epg_season_t *es;
+ epg_serieslink_t *es;
eit_event_t ev;
- lang_str_ele_t *ls;
if ( len < 12 ) return -1;
@@ -485,6 +484,16 @@ static int _eit_process_event
ptr += dlen;
}
+ /*
+ * Broadcast
+ */
+
+ /* Summary/Description */
+ if ( ev.summary )
+ *save |= epg_broadcast_set_summary2(ebc, ev.summary, mod);
+ if ( ev.desc )
+ *save |= epg_broadcast_set_description2(ebc, ev.desc, mod);
+
/* Broadcast Metadata */
*save |= epg_broadcast_set_is_hd(ebc, ev.hd, mod);
*save |= epg_broadcast_set_is_widescreen(ebc, ev.ws, mod);
@@ -492,51 +501,39 @@ static int _eit_process_event
*save |= epg_broadcast_set_is_subtitled(ebc, ev.st, mod);
*save |= epg_broadcast_set_is_deafsigned(ebc, ev.ds, mod);
- /* Find episode */
- if (*ev.uri) {
- ee = epg_episode_find_by_uri(ev.uri, 1, save);
- } else if ( !(ee = ebc->episode) ) {
- char *uri;
- uri = epg_hash(lang_str_get(ev.title, NULL),
- lang_str_get(ev.summary, NULL),
- lang_str_get(ev.desc, NULL));
- if (uri) {
- ee = epg_episode_find_by_uri(uri, 1, save);
- free(uri);
- }
+ /*
+ * Series link
+ */
+
+ if (*ev.suri) {
+ if ((es = epg_serieslink_find_by_uri(ev.suri, 1, save)))
+ *save |= epg_broadcast_set_serieslink(ebc, es, mod);
}
- /* Update Broadcast */
- if (ee) *save |= epg_broadcast_set_episode(ebc, ee, mod);
+ /*
+ * Episode
+ */
+
+ /* Find episode */
+ if (*ev.uri) {
+ if ((ee = epg_episode_find_by_uri(ev.uri, 1, save)))
+ *save |= epg_broadcast_set_episode(ebc, ee, mod);
+
+ /* Existing/Artificial */
+ } else
+ ee = epg_broadcast_get_episode(ebc, 1, save);
/* Update Episode */
if (ee) {
*save |= epg_episode_set_is_bw(ee, ev.bw, mod);
- if ( ev.title ) {
- RB_FOREACH(ls, ev.title, link)
- *save |= epg_episode_set_title(ee, ls->str, ls->lang, mod);
- }
- if ( ev.summary ) {
- RB_FOREACH(ls, ev.summary, link)
- *save |= epg_episode_set_summary(ee, ls->str, ls->lang, mod);
- }
- if ( ev.desc ) {
- RB_FOREACH(ls, ev.desc, link)
- *save |= epg_episode_set_description(ee, ls->str, ls->lang, mod);
- }
+ if ( ev.title )
+ *save |= epg_episode_set_title2(ee, ev.title, mod);
if ( ev.genre )
*save |= epg_episode_set_genre(ee, ev.genre, mod);
#if TODO_ADD_EXTRA
if ( ev.extra )
*save |= epg_episode_set_extra(ee, extra, mod);
#endif
-
- /* Season */
- if (*ev.suri) {
- es = epg_season_find_by_uri(ev.suri, 1, save);
- if (es)
- *save |= epg_episode_set_season(ee, es, mod);
- }
}
/* Tidy up */
diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c
index d98a2b05..a9a58466 100644
--- a/src/epggrab/module/opentv.c
+++ b/src/epggrab/module/opentv.c
@@ -51,7 +51,7 @@ typedef struct opentv_event
char *summary; ///< Event summary
char *desc; ///< Event description
uint8_t cat; ///< Event category
- uint16_t series; ///< Series (link) reference
+ char *series; ///< Series ID
uint8_t type; ///< 0x1=title, 0x2=summary
} opentv_event_t;
@@ -189,27 +189,6 @@ static epggrab_channel_t *_opentv_find_epggrab_channel
(epggrab_module_t*)mod);
}
-static epg_season_t *_opentv_find_season
- ( opentv_module_t *mod, int cid, opentv_event_t *ev, int *save )
-{
- char uri[64];
- sprintf(uri, "%s-%d-%d", mod->id, cid, ev->series);
- return epg_season_find_by_uri(uri, 1, save);
-}
-
-static epg_episode_t *_opentv_find_episode
- ( opentv_module_t *mod, int cid, opentv_event_t *ev, int *save )
-{
- char uri[100];
- char *tmp = epg_hash(ev->title, ev->summary, ev->desc);
- if (tmp) {
- snprintf(uri, 100, "%s-%d-%s", mod->id, cid, tmp);
- free(tmp);
- return epg_episode_find_by_uri(uri, 1, save);
- }
- return NULL;
-}
-
static service_t *_opentv_find_service ( int tsid, int sid )
{
th_dvb_adapter_t *tda;
@@ -278,8 +257,9 @@ static char *_opentv_parse_string
/* Parse a specific record */
static int _opentv_parse_event_record
( opentv_module_t *prov, opentv_event_t *ev, uint8_t *buf, int len,
- time_t mjd )
+ time_t mjd, channel_t *ch )
{
+ char series[256];
uint8_t rtag = buf[0];
uint8_t rlen = buf[1];
if (rlen+2 <= len) {
@@ -304,7 +284,11 @@ static int _opentv_parse_event_record
ev->desc = _opentv_parse_string(prov, buf+2, rlen);
break;
case 0xc1: // series link
- ev->series = ((uint16_t)buf[2] << 8) | buf[3];
+ if (!ev->series) {
+ sprintf(series, "opentv://%s/%hu", ch->ch_name,
+ ((uint16_t)buf[2] << 8) | buf[3]);
+ ev->series = strdup(series);
+ }
break;
default:
break;
@@ -317,7 +301,8 @@ static int _opentv_parse_event_record
static int _opentv_parse_event
( opentv_module_t *prov, opentv_status_t *sta,
uint8_t *buf, int len, int cid, time_t mjd,
- opentv_event_t *ev, int type )
+ opentv_event_t *ev, int type,
+ channel_t *ch )
{
int slen = ((int)buf[2] & 0xf << 8) | buf[3];
int i = 4;
@@ -337,7 +322,7 @@ static int _opentv_parse_event
/* Process records */
while (i < slen+4) {
- i += _opentv_parse_event_record(prov, ev, buf+i, len-i, mjd);
+ i += _opentv_parse_event_record(prov, ev, buf+i, len-i, mjd, ch);
}
return slen+4;
}
@@ -352,11 +337,10 @@ static int _opentv_parse_event_section
epggrab_channel_t *ec;
epg_broadcast_t *ebc;
epg_episode_t *ee;
- epg_season_t *es;
+ epg_serieslink_t *es;
opentv_event_t ev;
epggrab_module_t *src = (epggrab_module_t*)mod;
const char *lang = NULL;
- const char *str;
/* Get language (bit of a hack) */
if (!strcmp(mod->dict->id, "skyit")) lang = "it";
@@ -376,7 +360,12 @@ static int _opentv_parse_event_section
i = 7;
while (i < len) {
memset(&ev, 0, sizeof(opentv_event_t));
- i += _opentv_parse_event(mod, sta, buf+i, len-i, cid, mjd, &ev, type);
+ i += _opentv_parse_event(mod, sta, buf+i, len-i, cid, mjd,
+ &ev, type, ec->channel);
+
+ /*
+ * Broadcast
+ */
/* Find broadcast */
if (ev.type & OPENTV_TITLE) {
@@ -391,40 +380,35 @@ static int _opentv_parse_event_section
continue; // don't want to free() anything
}
- /* Find episode */
+ /* Summary / Description */
if (ebc) {
- ee = NULL;
+ if (ev.summary)
+ save |= epg_broadcast_set_summary(ebc, ev.summary, lang, src);
+ if (ev.desc)
+ save |= epg_broadcast_set_description(ebc, ev.desc, lang, src);
+ }
- /* Find episode */
- if (ev.type & OPENTV_SUMMARY || !ebc->episode)
- ee = _opentv_find_episode(mod, cid, &ev, &save);
+ /*
+ * Series link
+ */
- /* Use existing */
- if (!ee) ee = ebc->episode;
+ if (ebc && ev.series) {
+ if ((es = epg_serieslink_find_by_uri(ev.series, 1, &save)))
+ save |= epg_broadcast_set_serieslink(ebc, es, src);
+ }
- /* Update */
- if (ee) {
- if (!ev.title && ebc->episode) {
- if ((str = epg_episode_get_title(ebc->episode, NULL)))
- save |= epg_episode_set_title(ee, str, lang, NULL);
- } else if (ev.title)
- save |= epg_episode_set_title(ee, ev.title, lang, src);
- if (ev.summary)
- save |= epg_episode_set_summary(ee, ev.summary, lang, src);
- if (ev.desc)
- save |= epg_episode_set_description(ee, ev.desc, lang, src);
- if (ev.cat) {
- epg_genre_list_t *egl = calloc(1, sizeof(epg_genre_list_t));
- epg_genre_list_add_by_eit(egl, ev.cat);
- save |= epg_episode_set_genre(ee, egl, src);
- epg_genre_list_destroy(egl);
- }
- if (ev.series) {
- es = _opentv_find_season(mod, cid, &ev, &save);
- if (es) save |= epg_episode_set_season(ee, es, src);
- }
+ /*
+ * Episode
+ */
- save |= epg_broadcast_set_episode(ebc, ee, src);
+ if (ebc && (ee = epg_broadcast_get_episode(ebc, 1, &save))) {
+ if (ev.title)
+ save |= epg_episode_set_title(ee, ev.title, lang, src);
+ if (ev.cat) {
+ epg_genre_list_t *egl = calloc(1, sizeof(epg_genre_list_t));
+ epg_genre_list_add_by_eit(egl, ev.cat);
+ save |= epg_episode_set_genre(ee, egl, src);
+ epg_genre_list_destroy(egl);
}
}
@@ -432,6 +416,7 @@ static int _opentv_parse_event_section
if (ev.title) free(ev.title);
if (ev.summary) free(ev.summary);
if (ev.desc) free(ev.desc);
+ if (ev.series) free(ev.series);
}
/* Update EPG */
diff --git a/src/epggrab/module/xmltv.c b/src/epggrab/module/xmltv.c
index 3ff0892e..80dd9f57 100644
--- a/src/epggrab/module/xmltv.c
+++ b/src/epggrab/module/xmltv.c
@@ -182,20 +182,24 @@ static void parse_xmltv_ns_episode
static void parse_xmltv_dd_progid
(epggrab_module_t *mod, const char *s, char **uri, char **suri, int *en )
{
+ char buf[128];
if (strlen(s) < 2) return;
+
+ /* Raw URI */
+ snprintf(buf, sizeof(buf)-1, "ddprogid://%s/%s", mod->id, s);
/* SH - series without episode id so ignore */
- if (strncmp("SH", s, 2)) {
- *uri = malloc(strlen(mod->id) + 1 + strlen(s));
- sprintf(*uri, "%s-%s", mod->id, s);
- }
+ if (strncmp("SH", s, 2)) *uri = strdup(buf);
/* Episode */
if (!strncmp("EP", s, 2)) {
- int e = 0;
- while (s[e] && s[e] != '.') e++;
- *suri = hts_strndup(s, e);
- if (s[e] && s[e+1]) sscanf(s+e+1, "%d", en);
+ int e = strlen(buf);
+ while (e && s[e] != '.') e--;
+ if (e) {
+ buf[e] = '\0';
+ *suri = strdup(buf);
+ if (s[e+1]) sscanf(s+e+1, "%d", en);
+ }
}
}
@@ -358,7 +362,7 @@ static int _xmltv_parse_programme_tags
{
int save = 0, save2 = 0, save3 = 0;
epg_episode_t *ee = NULL;
- epg_season_t *es = NULL;
+ epg_serieslink_t *es = NULL;
epg_broadcast_t *ebc;
epg_genre_list_t *egl;
int sn = 0, sc = 0, en = 0, ec = 0, pn = 0, pc = 0;
@@ -367,22 +371,24 @@ static int _xmltv_parse_programme_tags
lang_str_t *title = NULL;
lang_str_t *desc = NULL;
lang_str_t *subtitle = NULL;
- lang_str_ele_t *ls;
/*
* Broadcast
*/
-
- /* Create/Find broadcast */
- if (!(ebc = epg_broadcast_find_by_time(ch, start, stop, 0, 1, &save2))) return 0;
+ if (!(ebc = epg_broadcast_find_by_time(ch, start, stop, 0, 1, &save)))
+ return 0;
stats->broadcasts.total++;
- if (save2) stats->broadcasts.created++;
+ if (save) stats->broadcasts.created++;
+
+ /* Description (wait for episode first) */
+ if (desc)
+ save3 |= epg_broadcast_set_description2(ebc, desc, mod);
/* Quality metadata */
- save2 |= parse_vid_quality(mod, ebc, ee, htsmsg_get_map(tags, "video"));
+ save |= parse_vid_quality(mod, ebc, ee, htsmsg_get_map(tags, "video"));
/* Accessibility */
- save2 |= xmltv_parse_accessibility(mod, ebc, tags);
+ save |= xmltv_parse_accessibility(mod, ebc, tags);
/* Misc */
if (htsmsg_get_map(tags, "previously-shown"))
@@ -391,66 +397,54 @@ static int _xmltv_parse_programme_tags
htsmsg_get_map(tags, "new"))
save |= epg_broadcast_set_is_new(ebc, 1, mod);
- /* Get episode info */
+ /*
+ * Episode/Series info
+ */
get_episode_info(mod, tags, &uri, &suri, &onscreen,
&sn, &sc, &en, &ec, &pn, &pc);
- _xmltv_parse_lang_str(&title, tags, "title");
- _xmltv_parse_lang_str(&desc, tags, "desc");
- _xmltv_parse_lang_str(&subtitle, tags, "sub-title");
/*
- * Season
+ * Series Link
*/
if (suri) {
- es = epg_season_find_by_uri(suri, 1, &save3);
+ es = epg_serieslink_find_by_uri(suri, 1, &save2);
free(suri);
if (es) stats->seasons.total++;
- if (save3) stats->seasons.created++;
+ if (save2) stats->seasons.created++;
+
+ if (es)
+ save |= epg_broadcast_set_serieslink(ebc, es, mod);
}
/*
* Episode
*/
- if (!uri && title) {
- uri = epg_hash(lang_str_get(title, NULL), NULL, lang_str_get(desc, NULL));
- }
if (uri) {
- ee = epg_episode_find_by_uri(uri, 1, &save);
+ if ((ee = epg_episode_find_by_uri(uri, 1, &save3)))
+ save |= epg_broadcast_set_episode(ebc, ee, mod);
free(uri);
+ } else {
+ ee = epg_broadcast_get_episode(ebc, 1, &save3);
}
+ if (ee) stats->episodes.total++;
+ if (save3) stats->episodes.created++;
- /* Update */
if (ee) {
- stats->episodes.total++;
- if (save) stats->episodes.created++;
+ _xmltv_parse_lang_str(&title, tags, "title");
+ _xmltv_parse_lang_str(&subtitle, tags, "sub-title");
- save2 |= epg_broadcast_set_episode(ebc, ee, mod);
-
- if (es)
- save |= epg_episode_set_season(ee, es, mod);
- if (title) {
- RB_FOREACH(ls, title, link) {
- save |= epg_episode_set_title(ee, ls->str, ls->lang, mod);
- }
- }
- if (subtitle) {
- RB_FOREACH(ls, subtitle, link) {
- save |= epg_episode_set_subtitle(ee, ls->str, ls->lang, mod);
- }
- }
- if (desc) {
- RB_FOREACH(ls, desc, link) {
- save |= epg_episode_set_description(ee, ls->str, ls->lang, mod);
- }
- }
+ if (title)
+ save3 |= epg_episode_set_title2(ee, title, mod);
+ if (subtitle)
+ save3 |= epg_episode_set_subtitle2(ee, subtitle, mod);
if ((egl = _xmltv_parse_categories(tags))) {
- save |= epg_episode_set_genre(ee, egl, mod);
+ save3 |= epg_episode_set_genre(ee, egl, mod);
epg_genre_list_destroy(egl);
}
- if (pn) save |= epg_episode_set_part(ee, pn, pc, mod);
- if (en) save |= epg_episode_set_number(ee, en, mod);
- if (save) stats->episodes.modified++;
+
+ if (pn) save3 |= epg_episode_set_part(ee, pn, pc, mod);
+ if (en) save3 |= epg_episode_set_number(ee, en, mod);
// TODO: need to handle certification and ratings
// TODO: need to handle season numbering!
@@ -458,7 +452,9 @@ static int _xmltv_parse_programme_tags
}
/* Stats */
- if (save2) stats->broadcasts.modified++;
+ if (save) stats->broadcasts.modified++;
+ if (save2) stats->seasons.modified++;
+ if (save3) stats->episodes.modified++;
/* Cleanup */
if (title) lang_str_destroy(title);
diff --git a/src/htsp.c b/src/htsp.c
index 7edce4a6..5d9d52a8 100644
--- a/src/htsp.c
+++ b/src/htsp.c
@@ -760,14 +760,14 @@ htsp_build_event(epg_broadcast_t *e)
htsmsg_add_u32(out, "channelId", e->channel->ch_id);
htsmsg_add_u32(out, "start", e->start);
htsmsg_add_u32(out, "stop", e->stop);
- if (e->episode) {
- if ((str = epg_episode_get_title(e->episode, NULL)))
- htsmsg_add_str(out, "title", str);
- if ((str = epg_episode_get_description(e->episode, NULL)))
- htsmsg_add_str(out, "description", str);
- else if((str = epg_episode_get_summary(e->episode, NULL)))
- htsmsg_add_str(out, "description", str);
+ if ((str = epg_broadcast_get_title(e, NULL)))
+ htsmsg_add_str(out, "title", str);
+ if ((str = epg_broadcast_get_description(e, NULL)))
+ htsmsg_add_str(out, "description", str);
+ else if((str = epg_broadcast_get_summary(e, NULL)))
+ htsmsg_add_str(out, "description", str);
+ if (e->episode) {
if((g = LIST_FIRST(&e->episode->genre)))
htsmsg_add_u32(out, "contentType", g->code);
}
diff --git a/src/webui/extjs.c b/src/webui/extjs.c
index c4e3df51..7f2c53fc 100644
--- a/src/webui/extjs.c
+++ b/src/webui/extjs.c
@@ -773,9 +773,9 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
if((s = epg_episode_get_subtitle(ee, lang)))
htsmsg_add_str(m, "subtitle", s);
- if((s = epg_episode_get_description(ee, lang)))
+ if((s = epg_broadcast_get_description(e, lang)))
htsmsg_add_str(m, "description", s);
- else if((s = epg_episode_get_summary(ee, lang)))
+ else if((s = epg_broadcast_get_summary(e, lang)))
htsmsg_add_str(m, "description", s);
if (epg_episode_number_format(ee, buf, 100, NULL, "Season %d", ".",
diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c
index 6aa8b29e..d3c5e895 100644
--- a/src/webui/simpleui.c
+++ b/src/webui/simpleui.c
@@ -124,7 +124,7 @@ page_simple(http_connection_t *hc,
rstatus = de != NULL ? val2str(de->de_sched_state,
recstatustxt) : NULL;
- s = epg_episode_get_title(e->episode, lang);
+ s = epg_broadcast_get_title(e, lang);
htsbuf_qprintf(hq,
""
"%02d:%02d-%02d:%02d %s%s%s
",
@@ -265,12 +265,11 @@ page_einfo(http_connection_t *hc, const char *remain, void *opaque)
}
htsbuf_qprintf(hq, "");
- if (e->episode) {
- if ( e->episode->description )
- htsbuf_qprintf(hq, "%s", lang_str_get(e->episode->description, NULL));
- else if ( e->episode->summary )
- htsbuf_qprintf(hq, "%s", lang_str_get(e->episode->summary, NULL));
- }
+
+ if ( (s = epg_broadcast_get_description(e, lang)) )
+ htsbuf_qprintf(hq, "%s", s);
+ else if ( (s = epg_broadcast_get_summary(e, lang)) )
+ htsbuf_qprintf(hq, "%s", s);
pthread_mutex_unlock(&global_lock);