]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Added the ability to map multiple EPG channels to the same TVH channel. Fixes #1163...
authorAdam Sutton <dev@adamsutton.me.uk>
Mon, 17 Sep 2012 10:09:07 +0000 (11:09 +0100)
committerAdam Sutton <dev@adamsutton.me.uk>
Mon, 17 Sep 2012 10:21:22 +0000 (11:21 +0100)
src/epggrab.h
src/epggrab/channel.c
src/epggrab/module.c
src/epggrab/module/opentv.c
src/epggrab/module/pyepg.c
src/epggrab/module/xmltv.c
src/webui/extjs.c
src/webui/static/app/chconf.js

index 9583e0ad0bc874e77e1d1e1d907e5eddec129bc6..ddb46a411d9d063b8cdcfcca16238aa7578ab466 100644 (file)
@@ -81,9 +81,15 @@ typedef struct epggrab_channel
   char                      *icon;    ///< Channel icon
   int                       number;   ///< Channel number
   
-  struct channel            *channel; ///< Mapped channel
+  LIST_HEAD(,epggrab_channel_link) channels; ///< Mapped channels
 } epggrab_channel_t;
 
+typedef struct epggrab_channel_link
+{
+  LIST_ENTRY(epggrab_channel_link) link;     ///< Link to grab channel
+  struct channel                   *channel; ///< Real channel
+} epggrab_channel_link_t;
+
 /*
  * Access functions
  */
index a3846bf9ecffdbf6d897f673ec88b32a002f32e8..db73b2884959772472bb669f49bcb4d3d3599a5d 100644 (file)
@@ -36,7 +36,7 @@
 int epggrab_channel_match ( epggrab_channel_t *ec, channel_t *ch )
 {
   if (!ec || !ch) return 0;
-  if (ec->channel) return 0; // ignore already paired
+  if (LIST_FIRST(&ec->channels)) return 0; // ignore already paired
 
   if (ec->name && !strcmp(ec->name, ch->ch_name)) return 1;
   return 0;
@@ -45,12 +45,18 @@ int epggrab_channel_match ( epggrab_channel_t *ec, channel_t *ch )
 /* Link epggrab channel to real channel */
 void epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
 {
+  epggrab_channel_link_t *ecl;
+
   /* No change */
-  if (!ch || ch == ec->channel) return;
+  if (!ch) return;
+  LIST_FOREACH(ecl, &ec->channels, link)
+    if (ecl->channel == ch) return;
 
   tvhlog(LOG_INFO, ec->mod->id, "linking %s to %s",
          ec->id, ch->ch_name);
-  ec->channel = ch;
+  ecl = calloc(1, sizeof(epggrab_channel_link_t));
+  ecl->channel = ch;
+  LIST_INSERT_HEAD(&ec->channels, ecl, link);
   if (ec->name && epggrab_channel_rename)
     channel_rename(ch, ec->name);
   if (ec->icon && epggrab_channel_reicon)
@@ -76,12 +82,14 @@ int epggrab_channel_match_and_link ( epggrab_channel_t *ec, channel_t *ch )
 int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
 {
   int save = 0;
+  epggrab_channel_link_t *ecl;
   if (!ec || !name) return 0;
   if (!ec->name || strcmp(ec->name, name)) {
     if (ec->name) free(ec->name);
     ec->name = strdup(name);
-    if (ec->channel && epggrab_channel_rename)
-      channel_rename(ec->channel, name);
+    if (epggrab_channel_rename)
+      LIST_FOREACH(ecl, &ec->channels, link)
+        channel_rename(ecl->channel, name);
     save = 1;
   }
   return save;
@@ -91,12 +99,14 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
 int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
 {
   int save = 0;
+  epggrab_channel_link_t *ecl;
   if (!ec->icon || strcmp(ec->icon, icon) ) {
   if (!ec | !icon) return 0;
     if (ec->icon) free(ec->icon);
     ec->icon = strdup(icon);
-    if (ec->channel && epggrab_channel_reicon)
-      channel_set_icon(ec->channel, icon);
+    if (epggrab_channel_reicon)
+      LIST_FOREACH(ecl, &ec->channels, link)
+        channel_set_icon(ecl->channel, icon);
     save = 1;
   }
   return save;
@@ -106,11 +116,13 @@ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
 int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
 {
   int save = 0;
+  epggrab_channel_link_t *ecl;
   if (!ec || (number <= 0)) return 0;
   if (ec->number != number) {
     ec->number = number;
-    if (ec->channel && epggrab_channel_renumber)
-      channel_set_number(ec->channel, number);
+    if (epggrab_channel_renumber)
+      LIST_FOREACH(ecl, &ec->channels, link)
+        channel_set_number(ecl->channel, number);
     save = 1;
   }
   return save;
@@ -123,7 +135,7 @@ void epggrab_channel_updated ( epggrab_channel_t *ec )
   if (!ec) return;
 
   /* Find a link */
-  if (!ec->channel)
+  if (!LIST_FIRST(&ec->channels))
     RB_FOREACH(ch, &channel_name_tree, ch_name_link)
       if (epggrab_channel_match_and_link(ec, ch)) break;
 
index facd3f78e59bfca932c210f2ffeef1ef4c9d0215..5eded322fe6b9d028a07e69c2dc30d32422e3711 100644 (file)
@@ -144,15 +144,19 @@ void epggrab_module_parse
 
 void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch )
 {
-  htsmsg_t *m = htsmsg_create_map();
+  htsmsg_t *a = NULL, *m = htsmsg_create_map();
   epggrab_module_t *mod = _m;
+  epggrab_channel_link_t *ecl;
 
   if (ch->name)
     htsmsg_add_str(m, "name", ch->name);
   if (ch->icon)
     htsmsg_add_str(m, "icon", ch->icon);
-  if (ch->channel)
-    htsmsg_add_u32(m, "channel", ch->channel->ch_id);
+  LIST_FOREACH(ecl, &ch->channels, link) {
+    if (!a) a = htsmsg_create_list();
+    htsmsg_add_u32(a, NULL, ecl->channel->ch_id);
+  }
+  if (a) htsmsg_add_msg(m, "channels", a);
   if (ch->number)
     htsmsg_add_u32(m, "number", ch->number);
 
@@ -171,12 +175,15 @@ void epggrab_module_ch_add ( void *m, channel_t *ch )
 void epggrab_module_ch_rem ( void *m, channel_t *ch )
 {
   epggrab_channel_t *egc;
+  epggrab_channel_link_t *egl;
   epggrab_module_int_t *mod = m;
   RB_FOREACH(egc, mod->channels, link) {
-    if (egc->channel == ch) {
-      egc->channel = NULL;
-      if (mod->ch_save) mod->ch_save(mod, egc);
-      break;
+    LIST_FOREACH(egl, &egc->channels, link) {
+      if (egl->channel == ch) {
+        LIST_REMOVE(egl, link);
+        free(egl);
+        break;
+      }
     }
   }
 }
@@ -192,18 +199,36 @@ static void _epggrab_module_channel_load
   int save = 0;
   const char *str;
   uint32_t u32;
+  htsmsg_t *a;
+  htsmsg_field_t *f;
+  channel_t *ch;
 
-  epggrab_channel_t *ch = epggrab_channel_find(mod->channels, id, 1, &save, mod);
+  epggrab_channel_t *egc
+    = epggrab_channel_find(mod->channels, id, 1, &save, mod);
 
   if ((str = htsmsg_get_str(m, "name")))
-    ch->name = strdup(str);
+    egc->name = strdup(str);
   if ((str = htsmsg_get_str(m, "icon")))
-    ch->icon = strdup(str);
+    egc->icon = strdup(str);
   if(!htsmsg_get_u32(m, "number", &u32))
-    ch->number = u32;
+    egc->number = u32;
+  if ((a = htsmsg_get_list(m, "channels"))) {
+    HTSMSG_FOREACH(f, a) {
+      if ((ch  = channel_find_by_identifier((uint32_t)f->hmf_s64))) {
+        epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t));
+        ecl->channel = ch;
+        LIST_INSERT_HEAD(&egc->channels, ecl, link);
+      }
+    }
 
-  if (!htsmsg_get_u32(m, "channel", &u32))
-    ch->channel = channel_find_by_identifier(u32);
+  /* Compat with older 3.1 code */
+  } else if (!htsmsg_get_u32(m, "channel", &u32)) {
+    if ((ch = channel_find_by_identifier(u32))) {
+      epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t));
+      ecl->channel = ch;
+      LIST_INSERT_HEAD(&egc->channels, ecl, link);
+    }
+  }
 }
 
 void epggrab_module_channels_load ( epggrab_module_t *mod )
index 2d7bdc4d10915673c5838523feb1eabf96f135ea..79f824d9e979855a3dac2f93fba90995d3d63f68 100644 (file)
@@ -336,6 +336,7 @@ static int _opentv_parse_event_section
   opentv_event_t ev;
   epggrab_module_t *src = (epggrab_module_t*)mod;
   const char *lang = NULL;
+  epggrab_channel_link_t *ecl;
 
   /* Get language (bit of a hack) */
   if      (!strcmp(mod->dict->id, "skyit"))  lang = "it";
@@ -344,8 +345,8 @@ static int _opentv_parse_event_section
   /* Channel */
   cid = ((int)buf[0] << 8) | buf[1];
   if (!(ec = _opentv_find_epggrab_channel(mod, cid, 0, NULL))) return 0;
-  if (!ec->channel) return 0;
-  if (!*ec->channel->ch_name) return 0; // ignore unnamed channels
+  if (!(ecl = LIST_FIRST(&ec->channels))) return 0;
+  // TODO: it's assumed that opentv channels are always 1-1 mapping!
   
   /* Time (start/stop referenced to this) */
   mjd = ((int)buf[5] << 8) | buf[6];
@@ -364,11 +365,11 @@ static int _opentv_parse_event_section
 
     /* Find broadcast */
     if (ev.type & OPENTV_TITLE) {
-      ebc = epg_broadcast_find_by_time(ec->channel, ev.start, ev.stop, ev.eid,
+      ebc = epg_broadcast_find_by_time(ecl->channel, ev.start, ev.stop, ev.eid,
                                        1, &save);
 
     /* Store */
-    } else if (!(ebc = epg_broadcast_find_by_eid(ec->channel, ev.eid))) {
+    } else if (!(ebc = epg_broadcast_find_by_eid(ecl->channel, ev.eid))) {
       opentv_event_t *skel = malloc(sizeof(opentv_event_t));
       memcpy(skel, &ev, sizeof(opentv_event_t));
       assert(!RB_INSERT_SORTED(&sta->events, skel, ev_link, _ev_cmp));
@@ -390,7 +391,7 @@ static int _opentv_parse_event_section
     if (ebc && ev.serieslink) {
       char suri[257];
       snprintf(suri, 256, "opentv://channel-%d/series-%d",
-               ec->channel->ch_id, ev.serieslink);
+               ecl->channel->ch_id, ev.serieslink);
       if ((es = epg_serieslink_find_by_uri(suri, 1, &save)))
         save |= epg_broadcast_set_serieslink(ebc, es, src);
     }
@@ -429,6 +430,7 @@ static void _opentv_parse_channels
   ( opentv_module_t *mod, uint8_t *buf, int len, uint16_t tsid )
 {
   epggrab_channel_t *ec;
+  epggrab_channel_link_t *ecl;
   service_t *svc;
   int sid, cid, cnum;
   int save = 0;
@@ -440,12 +442,14 @@ static void _opentv_parse_channels
 
     /* Find the service */
     svc = _opentv_find_service(tsid, sid);
-    if (svc && svc->s_ch) {
+    if (svc && svc->s_ch && service_is_primary_epg(svc)) {
       ec  =_opentv_find_epggrab_channel(mod, cid, 1, &save);
-      if (service_is_primary_epg(svc))
-        ec->channel = svc->s_ch;
-      else
-        ec->channel = NULL;
+      ecl = LIST_FIRST(&ec->channels);
+      if (!ecl) {
+        ecl = calloc(1, sizeof(epggrab_channel_link_t));
+        LIST_INSERT_HEAD(&ec->channels, ecl, link);
+      }
+      ecl->channel = svc->s_ch;
       save |= epggrab_channel_set_number(ec, cnum);
     }
     i += 9;
index 0eda3adef1f14c8af9449f676c151a445d8b7b33..abb3e26438ad21babfcfb3da2c5bf98c21cdf1d2 100644 (file)
@@ -354,6 +354,7 @@ static int _pyepg_parse_schedule
   htsmsg_field_t *f;
   epggrab_channel_t *ec;
   const char *str;
+  epggrab_channel_link_t *ecl;
 
   if ( data == NULL ) return 0;
 
@@ -361,12 +362,12 @@ static int _pyepg_parse_schedule
   if ((str  = htsmsg_get_str(attr, "channel")) == NULL) return 0;
   if ((ec   = _pyepg_channel_find(str, 0, NULL)) == NULL) return 0;
   if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0;
-  if (!ec->channel) return 0;
 
   HTSMSG_FOREACH(f, tags) {
     if (strcmp(f->hmf_name, "broadcast") == 0) {
-      save |= _pyepg_parse_broadcast(mod, htsmsg_get_map_by_field(f),
-                                     ec->channel, stats);
+      LIST_FOREACH(ecl, &ec->channels, link)
+        save |= _pyepg_parse_broadcast(mod, htsmsg_get_map_by_field(f),
+                                       ecl->channel, stats);
     }
   }
 
index 8ff3af0a091a224d51bbf0f19e79e05d6efe9fa0..3e0049b1504c0698f0cad67fbe09ecec07ac3a2f 100644 (file)
@@ -476,6 +476,7 @@ static int _xmltv_parse_programme
   const char *s, *chid;
   time_t start, stop;
   epggrab_channel_t *ch;
+  epggrab_channel_link_t *ecl;
 
   if(body == NULL) return 0;
 
@@ -483,7 +484,7 @@ static int _xmltv_parse_programme
   if((tags    = htsmsg_get_map(body,    "tags"))    == NULL) return 0;
   if((chid    = htsmsg_get_str(attribs, "channel")) == NULL) return 0;
   if((ch      = _xmltv_channel_find(chid, 0, NULL))   == NULL) return 0;
-  if (ch->channel == NULL) return 0;
+  if (!LIST_FIRST(&ch->channels)) return 0;
   if((s       = htsmsg_get_str(attribs, "start"))   == NULL) return 0;
   start = _xmltv_str2time(s);
   if((s       = htsmsg_get_str(attribs, "stop"))    == NULL) return 0;
@@ -491,8 +492,9 @@ static int _xmltv_parse_programme
 
   if(stop <= start || stop <= dispatch_clock) return 0;
 
-  save |= _xmltv_parse_programme_tags(mod, ch->channel, tags,
-                                      start, stop, stats);
+  LIST_FOREACH(ecl, &ch->channels, link)
+    save |= _xmltv_parse_programme_tags(mod, ecl->channel, tags,
+                                        start, stop, stats);
   return save;
 }
 
index 658b37c1f310479776ce537345e1e5ef55a9bc18..b4432c647793f8ef6e6f614b7a943687d5cb8ac9 100644 (file)
@@ -349,14 +349,19 @@ extjs_channels_update(htsmsg_t *in)
       char *modid, *ecid;
       epggrab_module_t *mod;
       epggrab_channel_t *ec;
+      epggrab_channel_link_t *ecl;
 
       /* Clear existing */
       LIST_FOREACH(mod, &epggrab_modules, link) {
         if (mod->type != EPGGRAB_OTA && mod->channels) {
           RB_FOREACH(ec, mod->channels, link) {
-            if (ec->channel == ch) {
-              ec->channel = NULL;
-              mod->ch_save(mod, ec);
+            LIST_FOREACH(ecl, &ec->channels, link) {
+              if (ecl->channel == ch) {
+                LIST_REMOVE(ecl, link);
+                free(ecl);
+                mod->ch_save(mod, ec);
+                break;
+              }
             }
           }
         }
@@ -400,6 +405,7 @@ extjs_channels(http_connection_t *hc, const char *remain, void *opaque)
   char *epggrabsrc;
   epggrab_module_t *mod;
   epggrab_channel_t *ec;
+  epggrab_channel_link_t *ecl;
 
   if(op == NULL)
     return 400;
@@ -438,15 +444,17 @@ extjs_channels(http_connection_t *hc, const char *remain, void *opaque)
       LIST_FOREACH(mod, &epggrab_modules, link) {
         if (mod->type != EPGGRAB_OTA && mod->channels) {
           RB_FOREACH(ec, mod->channels, link) {
-            if (ec->channel == ch) {
-              char id[100];
-              sprintf(id, "%s|%s", mod->id, ec->id);
-              if (!epggrabsrc) {
-                epggrabsrc = strdup(id);
-              } else {
-                epggrabsrc = realloc(epggrabsrc, strlen(epggrabsrc) + 2 + strlen(id));
-                strcat(epggrabsrc, ",");
-                strcat(epggrabsrc, id);
+            LIST_FOREACH(ecl, &ec->channels, link) {
+              if (ecl->channel == ch) {
+                char id[100];
+                sprintf(id, "%s|%s", mod->id, ec->id);
+                if (!epggrabsrc) {
+                  epggrabsrc = strdup(id);
+                } else {
+                  epggrabsrc = realloc(epggrabsrc, strlen(epggrabsrc) + 2 + strlen(id));
+                  strcat(epggrabsrc, ",");
+                  strcat(epggrabsrc, id);
+                }
               }
             }
           }
index 0e4b5435fe37ef4e574a27ba64e0dc9969313898..3a68d385c68ecbb7d52e8324dd17b4fff35c0502 100644 (file)
@@ -149,6 +149,7 @@ tvheadend.chconf = function() {
        }, {
                header : "EPG Grab source",
                dataIndex : 'epggrabsrc',
+    hiddenName : 'epggrabsrc',
                width : 150,
                editor : new Ext.ux.form.LovCombo({
                        loadingText : 'Loading...',