]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
channel: v2 - add possibility to reuse EPG from another channel, fixes #3217
authorJaroslav Kysela <perex@perex.cz>
Thu, 29 Oct 2015 14:49:01 +0000 (15:49 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 29 Oct 2015 14:49:01 +0000 (15:49 +0100)
src/channels.c
src/channels.h
src/epg.c
src/epg.h
src/epgdb.c

index b1301f71a9da549add424677bf60603aea29aa8b..52548c791384f33faa04348421e91038c496624c 100644 (file)
@@ -51,9 +51,12 @@ struct channel_tree channels;
 
 struct channel_tag_queue channel_tags;
 
+static int channel_in_load;
+
 static void channel_tag_init ( void );
 static void channel_tag_done ( void );
 static void channel_tag_mapping_destroy(idnode_list_mapping_t *ilm, void *origin);
+static void channel_epg_update_all ( channel_t *ch );
 
 static int
 ch_id_cmp ( channel_t *a, channel_t *b )
@@ -285,6 +288,45 @@ channel_class_bouquet_set ( void *o, const void *v )
   return 0;
 }
 
+static int
+channel_class_epg_parent_set_noupdate
+  ( void *o, const void *v, channel_t **parent )
+{
+  channel_t *ch = o;
+  const char *uuid = v;
+  if (strcmp(v ?: "", ch->ch_epg_parent ?: "")) {
+    if (ch->ch_epg_parent) {
+      LIST_REMOVE(ch, ch_epg_slave_link);
+      free(ch->ch_epg_parent);
+    }
+    ch->ch_epg_parent = NULL;
+    epg_channel_unlink(ch);
+    if (uuid && uuid[0] != '\0') {
+      if (channel_in_load) {
+        ch->ch_epg_parent = strdup(uuid);
+      } else {
+        *parent = channel_find_by_uuid(uuid);
+        if (*parent) {
+          ch->ch_epg_parent = strdup(uuid);
+          LIST_INSERT_HEAD(&(*parent)->ch_epg_slaves, ch, ch_epg_slave_link);
+        }
+      }
+    }
+    return 1;
+  }
+  return 0;
+}
+
+static int
+channel_class_epg_parent_set ( void *o, const void *v )
+{
+  channel_t *parent = NULL;
+  int save = channel_class_epg_parent_set_noupdate(o, v, &parent);
+  if (parent)
+    channel_epg_update_all(parent);
+  return save;
+}
+
 const idclass_t channel_class = {
   .ic_class      = "channel",
   .ic_caption    = N_("Channel"),
@@ -397,6 +439,15 @@ const idclass_t channel_class = {
       .list     = bouquet_class_get_list,
       .opts     = PO_RDONLY
     },
+    {
+      .type     = PT_STR,
+      .id       = "epg_parent",
+      .name     = N_("Reuse EPG from"),
+      .set      = channel_class_epg_parent_set,
+      .list     = channel_class_get_list,
+      .off      = offsetof(channel_t, ch_epg_parent),
+      .opts     = PO_ADVANCED
+    },
     {}
   }
 };
@@ -489,6 +540,31 @@ chtags_ok:
   return 1;
 }
 
+/**
+ *
+ */
+void
+channel_event_updated ( epg_broadcast_t *e )
+{
+  channel_t *ch;
+  int save;
+
+  LIST_FOREACH(ch, &e->channel->ch_epg_slaves, ch_epg_slave_link)
+    epg_broadcast_clone(ch, e, &save);
+}
+
+/**
+ *
+ */
+static void
+channel_epg_update_all ( channel_t *ch )
+{
+  epg_broadcast_t *e;
+
+  RB_FOREACH(e, &ch->ch_epg_schedule, sched_link)
+   channel_event_updated(e);
+}
+
 /* **************************************************************************
  * Property updating
  * *************************************************************************/
@@ -787,6 +863,7 @@ channel_delete ( channel_t *ch, int delconf )
 {
   th_subscription_t *s;
   idnode_list_mapping_t *ilm;
+  channel_t *ch1, *ch2;
 
   lock_assert(&global_lock);
 
@@ -815,6 +892,15 @@ channel_delete ( channel_t *ch, int delconf )
   /* EPG */
   epggrab_channel_rem(ch);
   epg_channel_unlink(ch);
+  for (ch1 = LIST_FIRST(&ch->ch_epg_slaves); ch1; ch1 = ch2) {
+    ch2 = LIST_NEXT(ch1, ch_epg_slave_link);
+    LIST_REMOVE(ch1, ch_epg_slave_link);
+    if (delconf) {
+      free(ch1->ch_epg_parent);
+      ch1->ch_epg_parent = NULL;
+      channel_save(ch1);
+    }
+  }
 
   /* HTSP */
   htsp_channel_delete(ch);
@@ -826,6 +912,7 @@ channel_delete ( channel_t *ch, int delconf )
   /* Free memory */
   RB_REMOVE(&channels, ch, ch_link);
   idnode_unlink(&ch->ch_id);
+  free(ch->ch_epg_parent);
   free(ch->ch_name);
   free(ch->ch_icon);
   free(ch);
@@ -848,6 +935,9 @@ channel_save ( channel_t *ch )
     epggrab_channel_add(ch);
 }
 
+
+
+
 /**
  *
  */
@@ -856,8 +946,11 @@ channel_init ( void )
 {
   htsmsg_t *c, *e;
   htsmsg_field_t *f;
-  RB_INIT(&channels);
+  channel_t *ch, *parent;
+  char *s;
   
+  RB_INIT(&channels);
+
   /* Tags */
   channel_tag_init();
 
@@ -865,11 +958,23 @@ channel_init ( void )
   if (!(c = hts_settings_load("channel/config")))
     return;
 
+  channel_in_load = 1;
   HTSMSG_FOREACH(f, c) {
     if (!(e = htsmsg_field_get_map(f))) continue;
     (void)channel_create(f->hmf_name, e, NULL);
   }
+  channel_in_load = 0;
   htsmsg_destroy(c);
+
+  /* Pair slave EPG, set parent again without channel_in_load */
+  CHANNEL_FOREACH(ch)
+    if ((s = ch->ch_epg_parent) != NULL) {
+      ch->ch_epg_parent = NULL;
+      channel_class_epg_parent_set_noupdate(ch, s, &parent);
+      free(s);
+    }
+  CHANNEL_FOREACH(ch)
+    channel_epg_update_all(ch);
 }
 
 /**
index 7a8c75508caab5ae2347518504ce18f4a9b5697c..49607d7765e7a08aa1bd1a99c87859efff60ddbc 100644 (file)
@@ -61,6 +61,9 @@ typedef struct channel
   LIST_HEAD(, th_subscription) ch_subscriptions;
 
   /* EPG fields */
+  char                 *ch_epg_parent;
+  LIST_HEAD(, channel)  ch_epg_slaves;
+  LIST_ENTRY(channel)   ch_epg_slave_link;
   epg_broadcast_tree_t  ch_epg_schedule;
   epg_broadcast_t      *ch_epg_now;
   epg_broadcast_t      *ch_epg_next;
@@ -68,7 +71,7 @@ typedef struct channel
   gtimer_t              ch_epg_timer_head;
   gtimer_t              ch_epg_timer_current;
 
-  int ch_epgauto;
+  int                   ch_epgauto;
   idnode_list_head_t    ch_epggrab;                /* 1 = epggrab channel, 2 = channel */
 
   /* DVR */
@@ -154,6 +157,8 @@ const char * channel_tag_get_icon(channel_tag_t *ct);
 
 int channel_access(channel_t *ch, struct access *a, int disabled);
 
+void channel_event_updated(epg_broadcast_t *e);
+
 int channel_tag_map(channel_tag_t *ct, channel_t *ch, void *origin);
 void channel_tag_unmap(channel_t *ch, void *origin);
 
index aae95c023b12b12146f1f9c6a7e7960604345218..6b0dddc8fedcf07cabccaf489f6149479b4438d6 100644 (file)
--- a/src/epg.c
+++ b/src/epg.c
@@ -210,7 +210,7 @@ static void _epg_object_create ( void *o )
   }
 }
 
-static epg_object_t *_epg_object_find_by_uri 
+static epg_object_t *_epg_object_find_by_uri
   ( const char *uri, int create, int *save,
     epg_object_tree_t *tree, epg_object_t **skel )
 {
@@ -1599,6 +1599,7 @@ static void _epg_broadcast_updated ( void *eo )
   }
   dvr_event_updated(eo);
   dvr_autorec_check_event(eo);
+  channel_event_updated(eo);
 }
 
 static epg_broadcast_t **_epg_broadcast_skel ( void )
@@ -1630,6 +1631,33 @@ epg_broadcast_t* epg_broadcast_find_by_time
   return _epg_channel_add_broadcast(channel, ebc, create, save);
 }
 
+epg_broadcast_t *epg_broadcast_clone
+  ( channel_t *channel, epg_broadcast_t *src, int *save )
+{
+  epg_broadcast_t *ebc;
+
+  if ( !src ) return NULL;
+  ebc = epg_broadcast_find_by_time(channel, src->start, src->stop,
+                                   src->dvb_eid, 1, save);
+  if (ebc) {
+    /* Copy metadata */
+    *save |= epg_broadcast_set_is_widescreen(ebc, src->is_widescreen, NULL);
+    *save |= epg_broadcast_set_is_hd(ebc, src->is_hd, NULL);
+    *save |= epg_broadcast_set_lines(ebc, src->lines, NULL);
+    *save |= epg_broadcast_set_aspect(ebc, src->aspect, NULL);
+    *save |= epg_broadcast_set_is_deafsigned(ebc, src->is_deafsigned, NULL);
+    *save |= epg_broadcast_set_is_subtitled(ebc, src->is_subtitled, NULL);
+    *save |= epg_broadcast_set_is_audio_desc(ebc, src->is_audio_desc, NULL);
+    *save |= epg_broadcast_set_is_new(ebc, src->is_new, NULL);
+    *save |= epg_broadcast_set_is_repeat(ebc, src->is_repeat, NULL);
+    *save |= epg_broadcast_set_summary2(ebc, src->summary, NULL);
+    *save |= epg_broadcast_set_description2(ebc, src->description, NULL);
+    *save |= epg_broadcast_set_serieslink(ebc, src->serieslink, NULL);
+    *save |= epg_broadcast_set_episode(ebc, src->episode, NULL);
+  }
+  return ebc;
+}
+
 epg_broadcast_t *epg_broadcast_find_by_id ( uint32_t id )
 {
   return (epg_broadcast_t*)epg_object_find_by_id(id, EPG_BROADCAST);
index c02b17b9fb3bdbbe2230a672ce633c13daaba0c3..7abdbe61cdc48abaa46b0cd320243e7a71d1f34e 100644 (file)
--- a/src/epg.h
+++ b/src/epg.h
@@ -457,6 +457,10 @@ epg_broadcast_t *epg_broadcast_find_by_time
 epg_broadcast_t *epg_broadcast_find_by_eid ( struct channel *ch, uint16_t eid );
 epg_broadcast_t *epg_broadcast_find_by_id  ( uint32_t id );
 
+/* Special */
+epg_broadcast_t *epg_broadcast_clone
+  ( struct channel *channel, epg_broadcast_t *src, int *save );
+
 /* Mutators */
 int epg_broadcast_set_episode
   ( epg_broadcast_t *b, epg_episode_t *e, struct epggrab_module *src )
index 47e19f3c26627441997fcf0f70483fbe50353904..f0a6b2f7064f71d7113e8bf20dfee3e269b1ce37 100644 (file)
@@ -362,6 +362,7 @@ void epg_save ( void )
   }
   if ( _epg_write_sect(sb, "broadcasts") ) goto error;
   CHANNEL_FOREACH(ch) {
+    if (ch->ch_epg_parent) continue;
     RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
       if (_epg_write(sb, epg_broadcast_serialize(ebc))) goto error;
       stats.broadcasts.total++;