]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
epg: implement epg set for serieslinks and episodelinks
authorJaroslav Kysela <perex@perex.cz>
Thu, 15 Feb 2018 14:51:30 +0000 (15:51 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 15 Feb 2018 16:09:49 +0000 (17:09 +0100)
src/api/api_epg.c
src/dvr/dvr_autorec.c
src/dvr/dvr_db.c
src/epg.c
src/epg.h
src/epgdb.c
src/epggrab/module/eit.c
src/epggrab/module/xmltv.c
src/htsp_server.c

index 2fba65cd5182c2088c04eaf07529570da683a4c8..82d4760ce6142924e325e88edefb72f11dd5ceec 100644 (file)
@@ -92,10 +92,10 @@ api_epg_entry ( epg_broadcast_t *eb, const char *lang, access_t *perm, const cha
 
   /* EPG IDs */
   htsmsg_add_u32(m, "eventId", eb->id);
-  if (eb->episode_uri && strncasecmp(eb->episode_uri, "tvh://", 6))
-    htsmsg_add_str(m, "episodeUri", eb->episode_uri);
-  if (eb->serieslink_uri)
-    htsmsg_add_str(m, "serieslinkUri", eb->serieslink_uri);
+  if (eb->episodelink && strncasecmp(eb->episodelink->uri, "tvh://", 6))
+    htsmsg_add_str(m, "episodeUri", eb->episodelink->uri);
+  if (eb->serieslink)
+    htsmsg_add_str(m, "serieslinkUri", eb->serieslink->uri);
   
   /* Channel Info */
   api_epg_add_channel(m, ch, *blank);
@@ -512,22 +512,21 @@ api_epg_episode_broadcasts
     uint32_t *entries, epg_broadcast_t *ebc_skip )
 {
   epg_broadcast_t *ebc;
-  channel_t *ch;
   htsmsg_t *m;
-  const char *uri = ep->episode_uri;
+  epg_set_t *episodelink = ep->episodelink;
+  epg_set_item_t *item;
 
-  if (uri == NULL || uri[0] == '\0')
+  if (episodelink == NULL)
     return;
 
-  CHANNEL_FOREACH(ch)
-    if (channel_access(ch, perm, 0))
-      RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link)
-        if (ebc && ebc->episode_uri && ebc != ebc_skip &&
-            strcmp(uri, ebc->episode_uri) == 0) {
-          m = api_epg_entry(ebc, lang, perm, NULL);
-          htsmsg_add_msg(l, NULL, m);
-          (*entries)++;
-        }
+  LIST_FOREACH(item, &episodelink->broadcasts, item_link) {
+    ebc = item->broadcast;
+    if (ebc != ebc_skip) {
+      m = api_epg_entry(ebc, lang, perm, NULL);
+      htsmsg_add_msg(l, NULL, m);
+      (*entries)++;
+    }
+  }
 }
 
 static int
@@ -566,8 +565,9 @@ api_epg_related
   uint32_t id, entries = 0;
   htsmsg_t *l = htsmsg_create_list(), *m;
   epg_broadcast_t *e, *ebc;
-  channel_t *ch;
-  char *lang, *uri;
+  char *lang;
+  epg_set_t *serieslink;
+  epg_set_item_t *item;
   
   if (htsmsg_get_u32(args, "eventId", &id))
     return -EINVAL;
@@ -576,17 +576,16 @@ api_epg_related
   lang = access_get_lang(perm, htsmsg_get_str(args, "lang"));
   pthread_mutex_lock(&global_lock);
   e = epg_broadcast_find_by_id(id);
-  uri = e->serieslink_uri;
-  if (uri && uri[0]) {
-    CHANNEL_FOREACH(ch)
-      if (channel_access(ch, perm, 0))
-        RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link)
-          if (ebc != e && ebc->serieslink_uri &&
-              strcmp(ebc->serieslink_uri, uri) == 0) {
-            m = api_epg_entry(ebc, lang, perm, NULL);
-            htsmsg_add_msg(l, NULL, m);
-            entries++;
-          }
+  serieslink = e->serieslink;
+  if (serieslink) {
+    LIST_FOREACH(item, &serieslink->broadcasts, item_link) {
+      ebc = item->broadcast;
+      if (ebc != e) {
+        m = api_epg_entry(ebc, lang, perm, NULL);
+        htsmsg_add_msg(l, NULL, m);
+        entries++;
+      }
+    }
   }
   pthread_mutex_unlock(&global_lock);
   free(lang);
index e140d72a9a6bfc690cb8fd25e64e41f9e9e89872..d9d3a63c71b33d21eadaae3ae5982b82bab57eb0 100644 (file)
@@ -168,8 +168,8 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
     return 0; // Avoid super wildcard match
 
   if(dae->dae_serieslink_uri) {
-    if (!e->serieslink_uri ||
-        strcmp(dae->dae_serieslink_uri ?: "", e->serieslink_uri)) return 0;
+    if (!e->serieslink ||
+        strcmp(dae->dae_serieslink_uri ?: "", e->serieslink->uri)) return 0;
   }
 
   if(dae->dae_btype != DVR_AUTOREC_BTYPE_ALL) {
@@ -401,8 +401,8 @@ dvr_autorec_add_series_link(const char *dvr_config_name,
   free(title);
   htsmsg_add_str(conf, "config_name", dvr_config_name ?: "");
   htsmsg_add_str(conf, "channel", chname);
-  if (event->serieslink_uri)
-    htsmsg_add_str(conf, "serieslink", event->serieslink_uri);
+  if (event->serieslink)
+    htsmsg_add_str(conf, "serieslink", event->serieslink->uri);
   htsmsg_add_str(conf, "owner", owner ?: "");
   htsmsg_add_str(conf, "creator", creator ?: "");
   htsmsg_add_str(conf, "comment", comment ?: "");
index 6fdf9d441794d2c71d6c1265a7b3012d2289601b..9d7ed609388a16f70668226a40982a9cd757cd05 100644 (file)
@@ -1059,8 +1059,8 @@ dvr_entry_create_from_htsmsg(htsmsg_t *conf, epg_broadcast_t *e)
       htsmsg_add_str(conf, "episode", s);
     if (e->copyright_year)
       htsmsg_add_u32(conf, "copyright_year", e->copyright_year);
-    if (e->episode_uri)
-      htsmsg_add_str(conf, "uri", e->episode_uri);
+    if (e->episodelink)
+      htsmsg_add_str(conf, "uri", e->episodelink->uri);
     if (e->image)
       htsmsg_add_str(conf, "image", e->image);
     genre = LIST_FIRST(&e->genre);
@@ -1099,7 +1099,7 @@ static time_t dvr_entry_get_segment_stop_extra( dvr_entry_t *de )
   time_t segment_stop_extra, start, stop, maximum_stop_time;
   int max_progs_to_check;
   epg_broadcast_t *e, *next;
-  const char *ep_uri, *next_uri;
+  epg_set_t *eplink1, *eplink2;
 
   if (!de)
     return 0;
@@ -1119,10 +1119,12 @@ static time_t dvr_entry_get_segment_stop_extra( dvr_entry_t *de )
     return 0;
   }
 
-  ep_uri = e->episode_uri;
+  eplink1 = e->episodelink;
 
   /* If not a segmented programme then no segment extra time */
-  if (!ep_uri || strncmp(ep_uri, "crid://", 7) || !strstr(ep_uri, "#"))
+  if (eplink1 == NULL ||
+      strncmp(eplink1->uri, "crid://", 7) ||
+      !strstr(eplink1->uri, "#"))
     return 0;
 
   /* This URI is a CRID (from EIT) which contains an IMI so the
@@ -1157,15 +1159,17 @@ static time_t dvr_entry_get_segment_stop_extra( dvr_entry_t *de )
        --max_progs_to_check && stop < maximum_stop_time &&
          next && next->start < stop + THREE_HOURS;
        next = epg_broadcast_get_next(next)) {
-    next_uri = next->episode_uri;
-    if (next_uri && strcmp(ep_uri, next_uri) == 0) {
+    eplink2 = next->episodelink;
+    if (eplink2 && eplink1 == eplink2) {
       /* Identical CRID+IMI. So that means that programme is a
        * segment part of this programme. So extend our stop time
        * to include this programme.
        */
       segment_stop_extra = next->stop - stop;
-      tvhinfo(LS_DVR, "Increasing stop for \"%s\" on \"%s\" \"%s\" by %"PRId64" seconds at start %"PRId64" and original stop %"PRId64,
-              lang_str_get(e->title, NULL), DVR_CH_NAME(de), ep_uri, (int64_t)segment_stop_extra, (int64_t)start, (int64_t)stop);
+      tvhinfo(LS_DVR, "Increasing stop for \"%s\" on \"%s\" \"%s\" by %"PRId64
+                      " seconds at start %"PRId64" and original stop %"PRId64,
+                      lang_str_get(e->title, NULL), DVR_CH_NAME(de), eplink1->uri,
+                      (int64_t)segment_stop_extra, (int64_t)start, (int64_t)stop);
     }
   }
 
index 3325b5e1b168efeccc0d71bc072ab9d131077094..c4b6efe9bdd9ef52ef2f1e1b9d09b8856b39a1b3 100644 (file)
--- a/src/epg.c
+++ b/src/epg.c
@@ -45,7 +45,8 @@
 epg_object_tree_t epg_objects[EPG_HASH_WIDTH];
 
 /* URI lists */
-epg_object_tree_t epg_episodes;
+epg_set_tree_t epg_serieslinks;
+epg_set_tree_t epg_episodelinks;
 
 /* Other special case lists */
 epg_object_list_t epg_object_unref;
@@ -114,7 +115,6 @@ static void _epg_object_destroy
   assert(eo->refcount == 0);
   tvhtrace(LS_EPG, "eo [%p, %u, %d] destroy",
            eo, eo->id, eo->type);
-  if (tree) RB_REMOVE(tree, eo, uri_link);
   if (eo->_updated) LIST_REMOVE(eo, up_link);
   RB_REMOVE(epg_id_tree(eo), eo, id_link);
 }
@@ -221,10 +221,10 @@ static htsmsg_t * _epg_object_serialize ( void *o )
   if (!eo->id || !eo->type) return NULL;
   m = htsmsg_create_map();
   htsmsg_add_u32(m, "id", eo->id);
-  htsmsg_add_u32(m, "type", eo->type);
+  htsmsg_add_u32(m, "tp", eo->type);
   if (eo->grabber)
-    htsmsg_add_str(m, "grabber", eo->grabber->id);
-  htsmsg_add_s64(m, "updated", eo->updated);
+    htsmsg_add_str(m, "gr", eo->grabber->id);
+  htsmsg_add_s64(m, "up", eo->updated);
   return m;
 }
 
@@ -233,12 +233,12 @@ static epg_object_t *_epg_object_deserialize ( htsmsg_t *m, epg_object_t *eo )
   int64_t s64;
   uint32_t u32;
   const char *s;
-  if (htsmsg_get_u32(m, "id",   &eo->id)) return NULL;
-  if (htsmsg_get_u32(m, "type", &u32))    return NULL;
+  if (htsmsg_get_u32(m, "id", &eo->id)) return NULL;
+  if (htsmsg_get_u32(m, "tp", &u32))    return NULL;
   if (u32 != eo->type)                    return NULL;
-  if ((s = htsmsg_get_str(m, "grabber")))
+  if ((s = htsmsg_get_str(m, "gr")))
     eo->grabber = epggrab_module_find_by_id(s);
-  if (!htsmsg_get_s64(m, "updated", &s64)) {
+  if (!htsmsg_get_s64(m, "up", &s64)) {
     _epg_object_set_updated(eo);
     eo->updated = s64;
   }
@@ -349,17 +349,17 @@ htsmsg_t *epg_episode_epnum_serialize ( epg_episode_num_t *num )
   if (!num) return NULL;
   m = htsmsg_create_map();
   if (num->e_num)
-    htsmsg_add_u32(m, "e_num", num->e_num);
+    htsmsg_add_u32(m, "enum", num->e_num);
   if (num->e_cnt)
-    htsmsg_add_u32(m, "e_cnt", num->e_cnt);
+    htsmsg_add_u32(m, "ecnt", num->e_cnt);
   if (num->s_num)
-    htsmsg_add_u32(m, "s_num", num->s_num);
+    htsmsg_add_u32(m, "snum", num->s_num);
   if (num->s_cnt)
-    htsmsg_add_u32(m, "s_cnt", num->s_cnt);
+    htsmsg_add_u32(m, "scnt", num->s_cnt);
   if (num->p_num)
-    htsmsg_add_u32(m, "p_num", num->p_num);
+    htsmsg_add_u32(m, "pnum", num->p_num);
   if (num->p_cnt)
-    htsmsg_add_u32(m, "p_cnt", num->p_cnt);
+    htsmsg_add_u32(m, "pcnt", num->p_cnt);
   if (num->text)
     htsmsg_add_str(m, "text", num->text);
   return m;
@@ -374,17 +374,17 @@ void epg_episode_epnum_deserialize
 
   memset(num, 0, sizeof(epg_episode_num_t));
 
-  if (!htsmsg_get_u32(m, "e_num", &u32))
+  if (!htsmsg_get_u32(m, "enum", &u32))
     num->e_num = u32;
-  if (!htsmsg_get_u32(m, "e_cnt", &u32))
+  if (!htsmsg_get_u32(m, "ecnt", &u32))
     num->e_cnt = u32;
-  if (!htsmsg_get_u32(m, "s_num", &u32))
+  if (!htsmsg_get_u32(m, "snum", &u32))
     num->s_num = u32;
-  if (!htsmsg_get_u32(m, "s_cnt", &u32))
+  if (!htsmsg_get_u32(m, "scnt", &u32))
     num->s_cnt = u32;
-  if (!htsmsg_get_u32(m, "p_num", &u32))
+  if (!htsmsg_get_u32(m, "pnum", &u32))
     num->p_num = u32;
-  if (!htsmsg_get_u32(m, "p_cnt", &u32))
+  if (!htsmsg_get_u32(m, "pcnt", &u32))
     num->p_cnt = u32;
   if ((str = htsmsg_get_str(m, "text")))
     num->text = strdup(str);
@@ -747,6 +747,71 @@ epg_broadcast_t *epg_match_now_next ( channel_t *ch, epg_broadcast_t *ebc )
   return ret;
 }
 
+/* **************************************************************************
+ * Broadcast set
+ * *************************************************************************/
+
+static int _set_uri_cmp(const void *_a, const void *_b)
+{
+  const epg_set_t *a = _a, *b = _b;
+  return strcmp(a->uri, b->uri);
+}
+
+epg_set_t *epg_set_broadcast_find_by_uri
+  (epg_set_tree_t *tree, const char *uri)
+{
+  epg_set_t *dummy;
+
+  if (tree == NULL || uri == NULL || uri[0] == '\0')
+    return NULL;
+  dummy = (epg_set_t *)(uri - offsetof(epg_set_t, uri));
+  return RB_FIND(tree, dummy, set_link, _set_uri_cmp);
+}
+
+
+epg_set_t *epg_set_broadcast_insert
+  (epg_set_tree_t *tree, epg_broadcast_t *b, const char *uri)
+{
+  epg_set_t *es;
+  epg_set_item_t *item;
+
+  if (tree == NULL || b == NULL)
+    return NULL;
+  if (uri == NULL || uri[0] == '\0')
+    return NULL;
+  es = epg_set_broadcast_find_by_uri(tree, uri);
+  if (!es) {
+    es = malloc(sizeof(*es) + strlen(uri) + 1);
+    LIST_INIT(&es->broadcasts);
+    strcpy(es->uri, uri);
+    RB_INSERT_SORTED(tree, es, set_link, _set_uri_cmp);
+  }
+  item = malloc(sizeof(*item));
+  item->broadcast = b;
+  LIST_INSERT_HEAD(&es->broadcasts, item, item_link);
+  return es;
+}
+
+void epg_set_broadcast_remove
+  (epg_set_tree_t *tree, epg_set_t *set, epg_broadcast_t *b)
+{
+  epg_set_item_t *item;
+
+  if (tree == NULL || set == NULL)
+    return;
+  LIST_FOREACH(item, &set->broadcasts, item_link)
+    if (item->broadcast == b) {
+      LIST_REMOVE(item, item_link);
+      free(item);
+      if (LIST_EMPTY(&set->broadcasts)) {
+        RB_REMOVE(tree, set, set_link);
+        free(set);
+      }
+      return;
+    }
+  assert(0);
+}
+
 /* **************************************************************************
  * Broadcast
  * *************************************************************************/
@@ -777,8 +842,8 @@ static void _epg_broadcast_destroy ( void *eo )
   if (ebc->category)    string_list_destroy(ebc->category);
   if (ebc->keyword)     string_list_destroy(ebc->keyword);
   if (ebc->keyword_cached) lang_str_destroy(ebc->keyword_cached);
-  free(ebc->serieslink_uri);
-  free(ebc->episode_uri);
+  epg_set_broadcast_remove(&epg_serieslinks, ebc->serieslink, ebc);
+  epg_set_broadcast_remove(&epg_episodelinks, ebc->episodelink, ebc);
   _epg_object_destroy(eo, NULL);
   assert(LIST_EMPTY(&ebc->dvr_entries));
   free(ebc);
@@ -875,7 +940,7 @@ int epg_broadcast_change_finish
   if (!(changes & EPG_CHANGED_SERIESLINK))
     save |= epg_broadcast_set_serieslink_uri(broadcast, NULL, NULL);
   if (!(changes & EPG_CHANGED_EPISODE))
-    save |= epg_broadcast_set_episode_uri(broadcast, NULL, NULL);
+    save |= epg_broadcast_set_episodelink_uri(broadcast, NULL, NULL);
   if (!(changes & EPG_CHANGED_DVB_EID))
     save |= epg_broadcast_set_dvb_eid(broadcast, 0, NULL);
   if (!(changes & EPG_CHANGED_IS_WIDESCREEN))
@@ -976,8 +1041,10 @@ epg_broadcast_t *epg_broadcast_clone
     *save |= epg_broadcast_set_category(ebc, src->category, &changes);
     *save |= epg_broadcast_set_keyword(ebc, src->keyword, &changes);
     *save |= epg_broadcast_set_description(ebc, src->description, &changes);
-    *save |= epg_broadcast_set_serieslink_uri(ebc, src->serieslink_uri, &changes);
-    *save |= epg_broadcast_set_episode_uri(ebc, src->episode_uri, &changes);
+    *save |= epg_broadcast_set_serieslink_uri
+               (ebc, src->serieslink ? src->serieslink->uri : NULL, &changes);
+    *save |= epg_broadcast_set_episodelink_uri
+               (ebc, src->episodelink ? src->episodelink->uri : NULL, &changes);
     *save |= epg_broadcast_set_first_aired(ebc, src->first_aired, &changes);
     *save |= epg_broadcast_set_copyright_year(ebc, src->copyright_year, &changes);
     _epg_object_set_grabber(ebc, src->grabber);
@@ -1012,32 +1079,38 @@ int epg_broadcast_set_running
   return save;
 }
 
+static int _epg_broadcast_set_set
+  ( epg_broadcast_t *ebc, const char *uri,
+    epg_set_tree_t *tree, epg_set_t **set )
+{
+  if (*set == NULL) {
+    if (uri == NULL || uri[0] == '\0')
+      return 0;
+  } else if (strcmp((*set)->uri ?: "", uri ?: "")) {
+    if (*set)
+      epg_set_broadcast_remove(tree, *set, ebc);
+  } else {
+    return 0;
+  }
+  *set = epg_set_broadcast_insert(tree, ebc, uri);
+  return 1;
+}
+
+
 int epg_broadcast_set_serieslink_uri
   ( epg_broadcast_t *ebc, const char *uri, epg_changes_t *changed )
 {
-  int save = 0;
   if (!ebc) return 0;
   if (changed) *changed |= EPG_CHANGED_SERIESLINK;
-  if (strcmp(ebc->serieslink_uri ?: "", uri ?: "")) {
-    free(ebc->serieslink_uri);
-    ebc->serieslink_uri = strdup(uri);
-    save = 1;
-  }
-  return save;
+  return _epg_broadcast_set_set(ebc, uri, &epg_serieslinks, &ebc->serieslink);
 }
 
-int epg_broadcast_set_episode_uri
+int epg_broadcast_set_episodelink_uri
   ( epg_broadcast_t *ebc, const char *uri, epg_changes_t *changed )
 {
-  int save = 0;
   if (!ebc) return 0;
-  if (changed) *changed |= EPG_CHANGED_SERIESLINK;
-  if (strcmp(ebc->episode_uri ?: "", uri ?: "")) {
-    free(ebc->episode_uri);
-    ebc->episode_uri = strdup(uri);
-    save = 1;
-  }
-  return save;
+  if (changed) *changed |= EPG_CHANGED_EPISODE;
+  return _epg_broadcast_set_set(ebc, uri, &epg_episodelinks, &ebc->episodelink);
 }
 
 int epg_broadcast_set_dvb_eid
@@ -1436,11 +1509,11 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
   htsmsg_add_s64(m, "start", broadcast->start);
   htsmsg_add_s64(m, "stop", broadcast->stop);
   if (broadcast->channel)
-    htsmsg_add_str(m, "channel", channel_get_uuid(broadcast->channel, ubuf));
+    htsmsg_add_str(m, "ch", channel_get_uuid(broadcast->channel, ubuf));
   if (broadcast->dvb_eid)
-    htsmsg_add_u32(m, "dvb_eid", broadcast->dvb_eid);
+    htsmsg_add_u32(m, "eid", broadcast->dvb_eid);
   if (broadcast->is_widescreen)
-    htsmsg_add_u32(m, "is_widescreen", 1);
+    htsmsg_add_u32(m, "is_wd", 1);
   if (broadcast->is_hd)
     htsmsg_add_u32(m, "is_hd", 1);
   if (broadcast->is_bw)
@@ -1450,49 +1523,52 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
   if (broadcast->aspect)
     htsmsg_add_u32(m, "aspect", broadcast->aspect);
   if (broadcast->is_deafsigned)
-    htsmsg_add_u32(m, "is_deafsigned", 1);
+    htsmsg_add_u32(m, "is_de", 1);
   if (broadcast->is_subtitled)
-    htsmsg_add_u32(m, "is_subtitled", 1);
+    htsmsg_add_u32(m, "is_st", 1);
   if (broadcast->is_audio_desc)
-    htsmsg_add_u32(m, "is_audio_desc", 1);
+    htsmsg_add_u32(m, "is_ad", 1);
   if (broadcast->is_new)
-    htsmsg_add_u32(m, "is_new", 1);
+    htsmsg_add_u32(m, "is_n", 1);
   if (broadcast->is_repeat)
-    htsmsg_add_u32(m, "is_repeat", 1);
+    htsmsg_add_u32(m, "is_r", 1);
   if (broadcast->star_rating)
-    htsmsg_add_u32(m, "star_rating", broadcast->star_rating);
+    htsmsg_add_u32(m, "star", broadcast->star_rating);
   if (broadcast->age_rating)
-    htsmsg_add_u32(m, "age_rating", broadcast->age_rating);
+    htsmsg_add_u32(m, "age", broadcast->age_rating);
   if (broadcast->image)
-    htsmsg_add_str(m, "image", broadcast->image);
+    htsmsg_add_str(m, "img", broadcast->image);
   if (broadcast->title)
-    lang_str_serialize(broadcast->summary, m, "title");
+    lang_str_serialize(broadcast->summary, m, "tit");
   if (broadcast->subtitle)
-    lang_str_serialize(broadcast->summary, m, "subtitle");
+    lang_str_serialize(broadcast->summary, m, "sti");
   if (broadcast->summary)
-    lang_str_serialize(broadcast->summary, m, "summary");
+    lang_str_serialize(broadcast->summary, m, "sum");
   if (broadcast->description)
-    lang_str_serialize(broadcast->description, m, "description");
-  htsmsg_add_msg(m, "epnum", epg_episode_epnum_serialize(&broadcast->epnum));
+    lang_str_serialize(broadcast->description, m, "des");
+  htsmsg_add_msg(m, "epn", epg_episode_epnum_serialize(&broadcast->epnum));
+  a = NULL;
   LIST_FOREACH(eg, &broadcast->genre, link) {
     if (!a) a = htsmsg_create_list();
     htsmsg_add_u32(a, NULL, eg->code);
   }
   if (a) htsmsg_add_msg(m, "genre", a);
   if (broadcast->copyright_year)
-    htsmsg_add_u32(m, "copyright_year", broadcast->copyright_year);
+    htsmsg_add_u32(m, "cyear", broadcast->copyright_year);
   if (broadcast->first_aired)
-    htsmsg_add_s64(m, "first_aired", broadcast->first_aired);
+    htsmsg_add_s64(m, "fair", broadcast->first_aired);
   if (broadcast->credits)
-    htsmsg_add_msg(m, "credits", htsmsg_copy(broadcast->credits));
+    htsmsg_add_msg(m, "cred", htsmsg_copy(broadcast->credits));
   /* No need to serialize credits_cached since it is rebuilt from credits. */
   if (broadcast->category)
-    string_list_serialize(broadcast->category, m, "category");
+    string_list_serialize(broadcast->category, m, "cat");
   if (broadcast->keyword)
-    string_list_serialize(broadcast->keyword, m, "keyword");
+    string_list_serialize(broadcast->keyword, m, "key");
   /* No need to serialize keyword_cached since it is rebuilt from keyword */
-  if (broadcast->serieslink_uri)
-    htsmsg_add_str(m, "serieslink", broadcast->serieslink_uri);
+  if (broadcast->serieslink)
+    htsmsg_add_str(m, "slink", broadcast->serieslink->uri);
+  if (broadcast->episodelink)
+    htsmsg_add_str(m, "elink", broadcast->episodelink->uri);
   return m;
 }
 
@@ -1516,7 +1592,6 @@ epg_broadcast_t *epg_broadcast_deserialize
   if (!start || !stop) return NULL;
   if (stop <= start) return NULL;
   if (stop <= gclk()) return NULL;
-  if (!(str = htsmsg_get_str(m, "episode"))) return NULL;
 
   _epg_object_deserialize(m, (epg_object_t*)*skel);
 
@@ -1525,7 +1600,7 @@ epg_broadcast_t *epg_broadcast_deserialize
   (*skel)->stop    = stop;
 
   /* Get channel */
-  if ((str = htsmsg_get_str(m, "channel")))
+  if ((str = htsmsg_get_str(m, "ch")))
     ch = channel_find(str);
   if (!ch) return NULL;
 
@@ -1534,9 +1609,9 @@ epg_broadcast_t *epg_broadcast_deserialize
   if (!ebc) return NULL;
 
   /* Get metadata */
-  if (!htsmsg_get_u32(m, "dvb_eid", &eid))
+  if (!htsmsg_get_u32(m, "eid", &eid))
     *save |= epg_broadcast_set_dvb_eid(ebc, eid, &changes);
-  if (!htsmsg_get_u32(m, "is_widescreen", &u32))
+  if (!htsmsg_get_u32(m, "is_wd", &u32))
     *save |= epg_broadcast_set_is_widescreen(ebc, u32, &changes);
   if (!htsmsg_get_u32(m, "is_hd", &u32))
     *save |= epg_broadcast_set_is_hd(ebc, u32, &changes);
@@ -1546,22 +1621,22 @@ epg_broadcast_t *epg_broadcast_deserialize
     *save |= epg_broadcast_set_lines(ebc, u32, &changes);
   if (!htsmsg_get_u32(m, "aspect", &u32))
     *save |= epg_broadcast_set_aspect(ebc, u32, &changes);
-  if (!htsmsg_get_u32(m, "is_deafsigned", &u32))
+  if (!htsmsg_get_u32(m, "is_de", &u32))
     *save |= epg_broadcast_set_is_deafsigned(ebc, u32, &changes);
-  if (!htsmsg_get_u32(m, "is_subtitled", &u32))
+  if (!htsmsg_get_u32(m, "is_st", &u32))
     *save |= epg_broadcast_set_is_subtitled(ebc, u32, &changes);
-  if (!htsmsg_get_u32(m, "is_audio_desc", &u32))
+  if (!htsmsg_get_u32(m, "is_ad", &u32))
     *save |= epg_broadcast_set_is_audio_desc(ebc, u32, &changes);
-  if (!htsmsg_get_u32(m, "is_new", &u32))
+  if (!htsmsg_get_u32(m, "is_n", &u32))
     *save |= epg_broadcast_set_is_new(ebc, u32, &changes);
-  if (!htsmsg_get_u32(m, "is_repeat", &u32))
+  if (!htsmsg_get_u32(m, "is_r", &u32))
     *save |= epg_broadcast_set_is_repeat(ebc, u32, &changes);
-  if (!htsmsg_get_u32(m, "star_rating", &u32))
+  if (!htsmsg_get_u32(m, "star", &u32))
     *save |= epg_broadcast_set_star_rating(ebc, u32, &changes);
-  if (!htsmsg_get_u32(m, "age_rating", &u32))
+  if (!htsmsg_get_u32(m, "age", &u32))
     *save |= epg_broadcast_set_age_rating(ebc, u32, &changes);
 
-  if ((str = htsmsg_get_str(m, "image")))
+  if ((str = htsmsg_get_str(m, "img")))
     *save |= epg_broadcast_set_image(ebc, str, &changes);
 
   if ((hm = htsmsg_get_list(m, "genre"))) {
@@ -1575,49 +1650,51 @@ epg_broadcast_t *epg_broadcast_deserialize
     epg_genre_list_destroy(egl);
   }
 
-  if ((ls = lang_str_deserialize(m, "title"))) {
+  if ((ls = lang_str_deserialize(m, "tit"))) {
     *save |= epg_broadcast_set_title(ebc, ls, &changes);
     lang_str_destroy(ls);
   }
-  if ((ls = lang_str_deserialize(m, "subtitle"))) {
+  if ((ls = lang_str_deserialize(m, "sti"))) {
     *save |= epg_broadcast_set_subtitle(ebc, ls, &changes);
     lang_str_destroy(ls);
   }
-  if ((ls = lang_str_deserialize(m, "summary"))) {
+  if ((ls = lang_str_deserialize(m, "sum"))) {
     *save |= epg_broadcast_set_summary(ebc, ls, &changes);
     lang_str_destroy(ls);
   }
-  if ((ls = lang_str_deserialize(m, "description"))) {
+  if ((ls = lang_str_deserialize(m, "des"))) {
     *save |= epg_broadcast_set_description(ebc, ls, &changes);
     lang_str_destroy(ls);
   }
 
-  if ((hm = htsmsg_get_map(m, "epnum"))) {
+  if ((hm = htsmsg_get_map(m, "epn"))) {
     epg_episode_epnum_deserialize(hm, &num);
     *save |= epg_broadcast_set_epnum(ebc, &num, &changes);
     if (num.text) free(num.text);
   }
 
-  if (!htsmsg_get_u32(m, "copyright_year", &u32))
+  if (!htsmsg_get_u32(m, "cyear", &u32))
     *save |= epg_broadcast_set_copyright_year(ebc, u32, &changes);
-  if (!htsmsg_get_s64(m, "first_aired", &s64))
+  if (!htsmsg_get_s64(m, "fair", &s64))
     *save |= epg_broadcast_set_first_aired(ebc, (time_t)s64, &changes);
 
-  if ((hm = htsmsg_get_map(m, "credits")))
+  if ((hm = htsmsg_get_map(m, "cred")))
     *save |= epg_broadcast_set_credits(ebc, hm, &changes);
 
-  if ((sl = string_list_deserialize(m, "keyword"))) {
+  if ((sl = string_list_deserialize(m, "key"))) {
     *save |= epg_broadcast_set_keyword(ebc, sl, &changes);
     string_list_destroy(sl);
   }
-  if ((sl = string_list_deserialize(m, "category"))) {
+  if ((sl = string_list_deserialize(m, "cat"))) {
     *save |= epg_broadcast_set_category(ebc, sl, &changes);
     string_list_destroy(sl);
   }
 
   /* Series link */
-  if ((str = htsmsg_get_str(m, "serieslink")))
+  if ((str = htsmsg_get_str(m, "slink")))
     *save |= epg_broadcast_set_serieslink_uri(ebc, str, &changes);
+  if ((str = htsmsg_get_str(m, "elink")))
+    *save |= epg_broadcast_set_episodelink_uri(ebc, str, &changes);
 
   *save |= epg_broadcast_change_finish(ebc, changes, 0);
 
@@ -2498,4 +2575,6 @@ void epg_skel_done(void)
 
   broad = _epg_broadcast_skel();
   free(*broad); *broad = NULL;
+  assert(RB_FIRST(&epg_serieslinks) == NULL);
+  assert(RB_FIRST(&epg_episodelinks) == NULL);
 }
index 294812cae4fa54c4ada712a0aa8ff3983f64e0fc..fc4ec0e99c0aee82f55ddcf536ca9abee1eb2a3f 100644 (file)
--- a/src/epg.h
+++ b/src/epg.h
@@ -40,6 +40,7 @@ typedef LIST_HEAD(,epg_object)     epg_object_list_t;
 typedef RB_HEAD  (,epg_object)     epg_object_tree_t;
 typedef LIST_HEAD(,epg_broadcast)  epg_broadcast_list_t;
 typedef RB_HEAD  (,epg_broadcast)  epg_broadcast_tree_t;
+typedef RB_HEAD  (,epg_set)        epg_set_tree_t;
 typedef LIST_HEAD(,epg_genre)      epg_genre_list_t;
 
 /*
@@ -48,8 +49,12 @@ typedef LIST_HEAD(,epg_genre)      epg_genre_list_t;
 typedef struct epg_genre           epg_genre_t;
 typedef struct epg_object          epg_object_t;
 typedef struct epg_broadcast       epg_broadcast_t;
+typedef struct epg_set_item        epg_set_item_t;
+typedef struct epg_set             epg_set_t;
 
 extern int epg_in_load;
+extern epg_set_tree_t epg_episodelinks;
+extern epg_set_tree_t epg_serieslinks;
 
 /*
  *
@@ -155,7 +160,6 @@ typedef struct epg_object_ops {
 /* Object */
 struct epg_object
 {
-  RB_ENTRY(epg_object)    uri_link;   ///< Global URI link
   RB_ENTRY(epg_object)    id_link;    ///< Global (ID) link
   LIST_ENTRY(epg_object)  un_link;    ///< Global unref'd link
   LIST_ENTRY(epg_object)  up_link;    ///< Global updated link
@@ -217,6 +221,28 @@ int epg_episode_number_cmp
 int epg_episode_number_cmpfull
   ( const epg_episode_num_t *a, const epg_episode_num_t *b );
 
+/* ************************************************************************
+ * Broadcast set
+ * ***********************************************************************/
+
+struct epg_set_item {
+  LIST_ENTRY(epg_set_item) item_link;
+  epg_broadcast_t *broadcast;
+};
+
+struct epg_set {
+  RB_ENTRY(epg_set) set_link;
+  LIST_HEAD(, epg_set_item) broadcasts;
+  char uri[0];
+};
+
+epg_set_t *epg_set_broadcast_insert
+  (epg_set_tree_t *tree, epg_broadcast_t *b, const char *uri);
+void epg_set_broadcast_remove
+  (epg_set_tree_t *tree, epg_set_t *set, epg_broadcast_t *b);
+epg_set_t *epg_set_broadcast_find_by_uri
+  (epg_set_tree_t *tree, const char *uri);
+
 /* ************************************************************************
  * Broadcast - specific airing (channel & time) of an episode
  * ***********************************************************************/
@@ -263,7 +289,7 @@ struct epg_broadcast
 
   char                      *image;            ///< Episode image
   epg_genre_list_t           genre;            ///< Episode genre(s)
-  epg_episode_num_t          epnum;            ///< Episode numbering
+  epg_episode_num_t          epnum;            ///< Episode numbering; NOTE: use the accessor routine!
 
   htsmsg_t                  *credits;          ///< Cast/Credits map of name -> role type (actor, presenter, director, etc).
   lang_str_t                *credits_cached;   ///< Comma separated cast (for regex searching in GUI/autorec). Kept in sync with cast_map
@@ -272,17 +298,15 @@ struct epg_broadcast
                                                ///< Used with drop-down lists in the GUI.
   string_list_t             *keyword;          ///< Extra keywords (typically from xmltv) such as "Wild West" or "Unicorn".
   lang_str_t                *keyword_cached;   ///< Cached CSV version for regex searches.
-  char                      *serieslink_uri;   ///< SeriesLink URI
-  char                      *episode_uri;      ///< Episode URI
-
-  // Note: do not use epnum directly! use the accessor routine
-
-  time_t                     first_aired;     ///< Original airdate
-  uint16_t                   copyright_year;  ///< xmltv DTD gives a tag "date" (separate to previously-shown/first aired).
-                                              ///< This is the date programme was "finished...probably the copyright date."
-                                              ///< We'll call it copyright_year since words like "complete" and "finished"
-                                              ///< sound too similar to dvr recorded functionality. We'll only store the
-                                              ///< year since we only get year not month and day.
+  epg_set_t                 *serieslink;       ///< Series Link
+  epg_set_t                 *episodelink;      ///< Episode Link
+
+  time_t                     first_aired;      ///< Original airdate
+  uint16_t                   copyright_year;   ///< xmltv DTD gives a tag "date" (separate to previously-shown/first aired).
+                                               ///< This is the date programme was "finished...probably the copyright date."
+                                               ///< We'll call it copyright_year since words like "complete" and "finished"
+                                               ///< sound too similar to dvr recorded functionality. We'll only store the
+                                               ///< year since we only get year not month and day.
 };
 
 /* Lookup */
@@ -358,7 +382,7 @@ int epg_broadcast_set_keyword
 int epg_broadcast_set_serieslink_uri
   ( epg_broadcast_t *b, const char *uri, epg_changes_t *changed )
   __attribute__((warn_unused_result));
-int epg_broadcast_set_episode_uri
+int epg_broadcast_set_episodelink_uri
   ( epg_broadcast_t *b, const char *uri, epg_changes_t *changed )
   __attribute__((warn_unused_result));
 int epg_broadcast_set_epnumber
@@ -421,7 +445,7 @@ size_t epg_broadcast_epnumber_format
 static inline int epg_episode_match(epg_broadcast_t *a, epg_broadcast_t *b)
 {
   if (a == NULL || b == NULL) return 0;
-  return strcmp(a->episode_uri ?: "", b->episode_uri ?: "") == 0;
+  return a->episodelink == b->episodelink;
 }
 
 /* Serialization */
index 9f91d140b1653f38dc96264bc9a914348764b4f7..d60848af348afefd52b4de85d9e070d81a090926 100644 (file)
@@ -90,8 +90,6 @@ static void epg_memoryinfo_broadcasts_update(memoryinfo_t *my)
       size += sizeof(*ebc);
       size += tvh_strlen(ebc->image);
       size += tvh_strlen(ebc->epnum.text);
-      size += tvh_strlen(ebc->episode_uri);
-      size += tvh_strlen(ebc->serieslink_uri);
       size += lang_str_size(ebc->title);
       size += lang_str_size(ebc->subtitle);
       size += lang_str_size(ebc->summary);
index a1dc23c269581c5016bfd506dd09b0381e47f888..410e1f6d385ff7e801535591bc63f4328dea183d 100644 (file)
@@ -610,7 +610,6 @@ static int _eit_process_event_one
   uint8_t running;
   epg_broadcast_t *ebc, _ebc;
   epg_running_t run;
-  lang_str_t *title_copy = NULL;
   epg_changes_t changes = 0;
   char tm1[32], tm2[32];
   int short_target = ((eit_module_t *)mod)->short_target;
@@ -646,12 +645,12 @@ static int _eit_process_event_one
     _ebc.dvb_eid = eid;
     _ebc.start = start;
     _ebc.stop = stop;
-    _ebc.episode_uri = ev->uri;
-    _ebc.serieslink_uri = ev->suri;
-    _ebc.title = title_copy = lang_str_copy(ev->title);
-
+    _ebc.episodelink = epg_set_broadcast_find_by_uri(&epg_episodelinks, ev->uri);
+    _ebc.serieslink = epg_set_broadcast_find_by_uri(&epg_serieslinks, ev->suri);
+    _ebc.title = lang_str_copy(ev->title);
     ebc = epg_match_now_next(ch, &_ebc);
     tvhtrace(mod->subsys, "%s:  running state only ebc=%p", svc->s_dvb_svcname ?: "(null)", ebc);
+    lang_str_destroy(_ebc.title);
     goto running;
   } else {
     *save = save2;
@@ -691,7 +690,7 @@ static int _eit_process_event_one
 
   /* Find episode */
   if (*ev->uri)
-    *save |= epg_broadcast_set_episode_uri(ebc, ev->suri, &changes);
+    *save |= epg_broadcast_set_episodelink_uri(ebc, ev->suri, &changes);
 
   /* Update Episode */
   if (ev->is_new > 0)
@@ -737,7 +736,6 @@ running:
     }
   }
 
-  if (title_copy) lang_str_destroy(title_copy);
   return 0;
 }
 
index accd4e474ea6824488deca03e4837ec73ac1f8dc..825807bc1d4ad6635fdfad95eb1ba02f58af6cc1 100644 (file)
@@ -613,9 +613,8 @@ static int _xmltv_parse_programme_tags
   epg_broadcast_t *ebc;
   epg_genre_list_t *egl;
   epg_episode_num_t epnum;
-  memset(&epnum, 0, sizeof(epnum));
+  epg_set_t *set;
   char *suri = NULL, *uri = NULL;
-  const char *s;
   lang_str_t *title = NULL;
   lang_str_t *desc = NULL;
   lang_str_t *summary = NULL;
@@ -623,6 +622,8 @@ static int _xmltv_parse_programme_tags
   time_t first_aired = 0;
   int8_t bw = -1;
 
+  memset(&epnum, 0, sizeof(epnum));
+
   /*
    * Broadcast
    */
@@ -716,12 +717,12 @@ static int _xmltv_parse_programme_tags
    * Series Link
    */
   if (suri) {
-    s = ebc->serieslink_uri;
+    set = ebc->serieslink;
     save |= epg_broadcast_set_serieslink_uri(ebc, suri, &changes);
     free(suri);
     stats->seasons.total++;
     if (changes & EPG_CHANGED_SERIESLINK) {
-      if (s == NULL)
+      if (set == NULL)
         stats->seasons.created++;
       else
         stats->seasons.modified++;
@@ -732,11 +733,11 @@ static int _xmltv_parse_programme_tags
    * Episode
    */
   if (uri) {
-    s = ebc->episode_uri;
-    save |= epg_broadcast_set_episode_uri(ebc, uri, &changes);
+    set = ebc->episodelink;
+    save |= epg_broadcast_set_episodelink_uri(ebc, uri, &changes);
     stats->episodes.total++;
     if (changes & EPG_CHANGED_EPISODE) {
-      if (s == NULL)
+      if (set == NULL)
         stats->episodes.created++;
       else
         stats->episodes.modified++;
index 291f2e16c90f922999431330886490c637f226ef..e723d00b7a54511d135724f76809694d9d8fb0c3 100644 (file)
@@ -1287,12 +1287,12 @@ htsp_build_event
     htsmsg_add_msg(out, "keyword", string_list_to_htsmsg(e->keyword));
   }
 
-  if (e->serieslink_uri)
-    htsmsg_add_str(out, "serieslinkUri", e->serieslink_uri);
+  if (e->serieslink)
+    htsmsg_add_str(out, "serieslinkUri", e->serieslink->uri);
 
   /* tvh:// uris are internal */
-  if (e->episode_uri && strncasecmp(e->episode_uri, "tvh://", 6))
-    htsmsg_add_str(out, "episodeUri", e->episode_uri);
+  if (e->episodelink && strncasecmp(e->episodelink->uri, "tvh://", 6))
+    htsmsg_add_str(out, "episodeUri", e->episodelink->uri);
 
   if((g = LIST_FIRST(&e->genre))) {
     uint32_t code = g->code;