]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
channel: migrated channel_t to an idnode
authorAdam Sutton <dev@adamsutton.me.uk>
Tue, 20 Aug 2013 15:21:02 +0000 (16:21 +0100)
committerAdam Sutton <dev@adamsutton.me.uk>
Thu, 22 Aug 2013 14:15:08 +0000 (15:15 +0100)
Also since the channel name is no longer unique various other things have
had to be updated.

32 files changed:
Makefile
src/api.c
src/api.h
src/api/api_channel.c [new file with mode: 0644]
src/api/api_idnode.c
src/api/api_service.c
src/channels.c
src/channels.h
src/dvr/dvr_autorec.c
src/dvr/dvr_db.c
src/epg.c
src/epgdb.c
src/epggrab/channel.c
src/epggrab/module.c
src/htsp_server.c
src/idnode.c
src/idnode.h
src/input/mpegts/tsfile/tsfile.c
src/main.c
src/prop.c
src/prop.h
src/service.c
src/service.h
src/service_mapper.c
src/service_mapper.h
src/tvheadend.h
src/webui/extjs.c
src/webui/statedump.c
src/webui/static/app/chconf.js
src/webui/static/app/idnode.js
src/webui/static/app/tvheadend.js
src/webui/webui.c

index 72a6ba718836ff18b9a4ea1eb4bdf5a68e06de50..f18334f7b1aaca655a1436f733d10f3dd82cb492 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -112,6 +112,7 @@ SRCS =  src/version.c \
 SRCS += \
        src/api.c \
        src/api/api_idnode.c \
+       src/api/api_channel.c \
        src/api/api_service.c \
        src/api/api_mpegts.c \
 
index 20a5c7750ee6327e6ed8b07ec0be71dafc4b4492..2a0b4b6714a5d8dc8153e6406839aef4c1a31182 100644 (file)
--- a/src/api.c
+++ b/src/api.c
@@ -118,4 +118,5 @@ void api_init ( void )
   api_idnode_init();
   api_mpegts_init();
   api_service_init();
+  api_channel_init();
 }
index 0933c36eca236cecc76b775850a5f24ee8e91176..ca4be19ed2270f62194680155a43e1b9d8e1a50a 100644 (file)
--- a/src/api.h
+++ b/src/api.h
@@ -59,6 +59,7 @@ void api_init         ( void );
 void api_idnode_init  ( void );
 void api_mpegts_init  ( void );
 void api_service_init ( void );
+void api_channel_init ( void );
 
 /*
  * IDnode
@@ -85,4 +86,7 @@ int api_idnode_class
 int api_idnode_tree
   ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
 
+int api_idnode_load_by_class
+  ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
+
 #endif /* __TVH_API_H__ */
diff --git a/src/api/api_channel.c b/src/api/api_channel.c
new file mode 100644 (file)
index 0000000..038c229
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  API - channel related calls
+ *
+ *  Copyright (C) 2013 Adam Sutton
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TVH_API_SERVICE_H__
+#define __TVH_API_SERVICE_H__
+
+#include "tvheadend.h"
+#include "channels.h"
+#include "access.h"
+#include "api.h"
+
+// TODO: this will need converting to an idnode system
+static int
+api_channel_list
+  ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+  channel_t *ch;
+  htsmsg_t *l, *e;
+
+  l = htsmsg_create_list();
+  pthread_mutex_lock(&global_lock);
+  CHANNEL_FOREACH(ch) {
+    e = htsmsg_create_map();
+    htsmsg_add_str(e, "key", idnode_uuid_as_str(&ch->ch_id));
+    htsmsg_add_str(e, "val", ch->ch_name ?: "");
+    htsmsg_add_msg(l, NULL, e);
+  }
+  pthread_mutex_unlock(&global_lock);
+  *resp = htsmsg_create_map();
+  htsmsg_add_msg(*resp, "entries", l);
+  
+  return 0;
+}
+
+static void
+api_channel_grid
+  ( idnode_set_t *ins, api_idnode_grid_conf_t *conf )
+{
+  channel_t *ch;
+
+  CHANNEL_FOREACH(ch)
+    idnode_set_add(ins, (idnode_t*)ch, &conf->filter);
+}
+
+void api_channel_init ( void )
+{
+  static api_hook_t ah[] = {
+    { "channel/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&channel_class },
+    { "channel/grid",  ACCESS_ANONYMOUS, api_idnode_grid,  api_channel_grid },
+    { "channel/list",  ACCESS_ANONYMOUS, api_channel_list, NULL },
+    { NULL },
+  };
+
+  api_register_all(ah);
+}
+
+
+#endif /* __TVH_API_IDNODE_H__ */
index 42a5f1c82cb1cb3f6afed8d53a77015b57ad58c6..3554bc2a660dc20791c20c0d2c0e56d527e46b69 100644 (file)
@@ -133,9 +133,9 @@ api_idnode_grid
   return 0;
 }
 
-static int
+int
 api_idnode_load_by_class
-  ( const char *class, htsmsg_t *args, htsmsg_t **resp )
+  ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
 {
   int i, _enum;
   const idclass_t *idc;
@@ -149,10 +149,8 @@ api_idnode_load_by_class
   pthread_mutex_lock(&global_lock);
 
   /* Find class */
-  if (!(idc = idclass_find(class))) {
-    pthread_mutex_unlock(&global_lock);
-    return EINVAL;
-  }
+  idc = opaque;
+  assert(idc);
 
   l = htsmsg_create_list();
   if ((is = idnode_find_all(idc))) {
@@ -162,7 +160,7 @@ api_idnode_load_by_class
       /* Name/UUID only */
       if (_enum) {
         e = htsmsg_create_map();
-        htsmsg_add_str(e, "key",  idnode_uuid_as_str(in));
+        htsmsg_add_str(e, "key", idnode_uuid_as_str(in));
         htsmsg_add_str(e, "val", idnode_get_title(in));
 
       /* Full record */
@@ -192,8 +190,16 @@ api_idnode_load
   const char *uuid, *class;
 
   /* Class based */
-  if ((class = htsmsg_get_str(args, "class")))
-    return api_idnode_load_by_class(class, args, resp);
+  if ((class = htsmsg_get_str(args, "class"))) {
+    const idclass_t *idc;
+    pthread_mutex_lock(&global_lock);
+    idc = idclass_find(class);
+    pthread_mutex_unlock(&global_lock);
+    if (!idc)
+      return EINVAL;
+    // TODO: bit naff that 2 locks are required here
+    return api_idnode_load_by_class((void*)idc, NULL, args, resp);
+  }
   
   /* UUIDs */
   if (!(f = htsmsg_field_find(args, "uuid")))
index 89a717f03ab9594e467dcf31292048de2374529a..2f60553482713603c7bb321a019c23838df7841f 100644 (file)
@@ -21,6 +21,7 @@
 #define __TVH_API_SERVICE_H__
 
 #include "tvheadend.h"
+#include "service.h"
 #include "service_mapper.h"
 #include "access.h"
 #include "api.h"
@@ -75,10 +76,13 @@ api_mapper_status
 
 void api_service_init ( void )
 {
+  extern const idclass_t service_class;
   static api_hook_t ah[] = {
     { "service/mapper/start",   ACCESS_ADMIN, api_mapper_start,  NULL },
     { "service/mapper/stop",    ACCESS_ADMIN, api_mapper_stop,   NULL },
     { "service/mapper/status",  ACCESS_ADMIN, api_mapper_status, NULL },
+    { "service/list",           ACCESS_ANONYMOUS, api_idnode_load_by_class, 
+      (void*)&service_class },
     { NULL },
   };
 
index bc75e6480b275d67af82cf26157a983eadb748ff..80d6983e957b832b8048306f349fcda56c970ffa 100644 (file)
 #include "htsp_server.h"
 #include "imagecache.h"
 #include "service_mapper.h"
+#include "htsbuf.h"
+
+struct channel_tree channels;
 
-struct channel_tree channel_name_tree;
-static struct channel_tree channel_identifier_tree;
 struct channel_tag_queue channel_tags;
 static dtable_t *channeltags_dtable;
 
+static void channel_tag_init ( void );
 static channel_tag_t *channel_tag_find(const char *id, int create);
 static void channel_tag_mapping_destroy(channel_tag_mapping_t *ctm, 
                                        int flags);
@@ -53,520 +55,389 @@ static void channel_tag_mapping_destroy(channel_tag_mapping_t *ctm,
 #define CTM_DESTROY_UPDATE_TAG     0x1
 #define CTM_DESTROY_UPDATE_CHANNEL 0x2
 
+static int
+ch_id_cmp ( channel_t *a, channel_t *b )
+{
+  return channel_get_id(a) - channel_get_id(b);
+}
+
+/* **************************************************************************
+ * Class definition
+ * *************************************************************************/
 
-/**
- *
- */
 static void
-channel_list_changed(void)
+channel_class_save ( idnode_t *self )
 {
-  htsmsg_t *m = htsmsg_create_map();
-  htsmsg_add_u32(m, "reload", 1);
-  notify_by_msg("channels", m);
+  channel_save((channel_t*)self);
 }
 
-
-static int
-dictcmp(const char *a, const char *b)
+static const void *
+channel_class_services_get ( void *obj )
 {
-  long int da, db;
-
-  while(1) {
-    switch((*a >= '0' && *a <= '9' ? 1 : 0)|(*b >= '0' && *b <= '9' ? 2 : 0)) {
-    case 0:  /* 0: a is not a digit, nor is b */
-      if(*a != *b)
-       return *(const unsigned char *)a - *(const unsigned char *)b;
-      if(*a == 0)
-       return 0;
-      a++;
-      b++;
-      break;
-    case 1:  /* 1: a is a digit,  b is not */
-    case 2:  /* 2: a is not a digit,  b is */
-       return *(const unsigned char *)a - *(const unsigned char *)b;
-    case 3:  /* both are digits, switch to integer compare */
-      da = strtol(a, (char **)&a, 10);
-      db = strtol(b, (char **)&b, 10);
-      if(da != db)
-       return da - db;
-      break;
-    }
+  static char *s = NULL;
+  const char *uuid;
+  int first = 1;
+  channel_t *ch = obj;
+  htsbuf_queue_t hq;
+  channel_service_mapping_t *csm;
+  
+  /* Free previous */
+  if (s) free(s);
+  s = NULL;
+
+  /* Add all */
+  LIST_FOREACH(csm, &ch->ch_services, csm_svc_link) {
+    if (first)
+      htsbuf_queue_init(&hq, 0);
+    else
+      htsbuf_append(&hq, ",", 1);
+    uuid = idnode_uuid_as_str(&csm->csm_svc->s_id);
+    htsbuf_append(&hq, uuid, strlen(uuid));
+    first = 0;
   }
-}
 
+  /* Build string */
+  if (!first) {
+    s = htsbuf_to_string(&hq);  
+    htsbuf_queue_flush(&hq);
+  }
 
-/**
- *
- */
-static int
-channelcmp(const channel_t *a, const channel_t *b)
-{
-  return dictcmp(a->ch_name, b->ch_name);
+  return &s;
 }
 
-
-/**
- *
- */
 static int
-chidcmp(const channel_t *a, const channel_t *b)
+channel_class_services_set ( void *obj, const void *p )
 {
-  return a->ch_id - b->ch_id;
+  return channel_set_services_by_list(obj, p);
 }
 
-/**
- *
- */
-static void
-channel_set_name(channel_t *ch, const char *name)
+static htsmsg_t *
+channel_class_services_enum ( void *obj )
 {
-  const char *n2;
-  int l, i;
-  char *cp, c;
-
-  free((void *)ch->ch_name);
-  free((void *)ch->ch_sname);
-
-  ch->ch_name = strdup(name);
-
-  l = strlen(name);
-  ch->ch_sname = cp = malloc(l + 1);
-
-  n2 = strdup(name);
-
-  for(i = 0; i < strlen(n2); i++) {
-    c = tolower(n2[i]);
-    if(isalnum(c))
-      *cp++ = c;
+  htsmsg_t *e, *m = htsmsg_create_map();
+  htsmsg_add_str(m, "type",  "api");
+  htsmsg_add_str(m, "uri",   "service/list");
+  htsmsg_add_str(m, "event", "service");
+  e = htsmsg_create_map();
+  htsmsg_add_u32(e, "enum", 1);
+  htsmsg_add_msg(m, "params", e);
+  return m;
+}
+
+static const void *
+channel_class_tags_get ( void *obj )
+{
+  static char *s = NULL;
+  int first = 1;
+  channel_t *ch = obj;
+  htsbuf_queue_t hq;
+  channel_tag_mapping_t *ctm;
+  
+  /* Free previous */
+  if (s) free(s);
+  s = NULL;
+
+  /* Add all */
+  LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
+    if (first)
+      htsbuf_queue_init(&hq, 0);
     else
-      *cp++ = '_';
+      htsbuf_append(&hq, ",", 1);
+    htsbuf_append(&hq, ctm->ctm_tag->ct_name, strlen(ctm->ctm_tag->ct_name));
+    first = 0;
   }
-  *cp = 0;
-
-  free((void *)n2);
-
-  RB_INSERT_SORTED(&channel_name_tree, ch, ch_name_link, channelcmp);
-  //assert(x == NULL);
 
-  /* Notify clients */
-  channel_list_changed();
+  /* Build string */
+  if (!first) {
+    s = htsbuf_to_string(&hq);  
+    htsbuf_queue_flush(&hq);
+  }
 
+  return &s;
 }
 
-
-/**
- *
- */
-static channel_t *
-channel_create2(const char *name, int number)
+static int
+channel_class_tags_set ( void *obj, const void *p )
 {
-  channel_t *ch, *x;
-  int id;
-  char buf[32];
-
-  ch = RB_LAST(&channel_identifier_tree);
-  if(ch == NULL) {
-    id = 1;
-  } else {
-    id = ch->ch_id + 1;
-  }
-
-  if (!name || !*name) {
-    snprintf(buf, sizeof(buf), "Channel %d", id);
-    name = buf;
-  }
-
-  ch = calloc(1, sizeof(channel_t));
-  channel_set_name(ch, name);
-  ch->ch_number = number;
-
-  ch->ch_id = id;
-  x = RB_INSERT_SORTED(&channel_identifier_tree, ch, 
-                      ch_identifier_link, chidcmp);
-
-  assert(x == NULL);
-
-  epggrab_channel_add(ch);
-
-  htsp_channel_add(ch);
-  return ch;
+  return channel_set_tags_by_list(obj, p);
 }
 
-/**
- *
- */
-channel_t *
-channel_create ( const char *name )
+static htsmsg_t *
+channel_class_tags_enum ( void *obj )
 {
-  channel_t *ch = channel_create2(name, 0);
-  channel_save(ch);
-  return ch;
+  channel_tag_t *ct;
+  htsmsg_t *m = htsmsg_create_list();
+  TAILQ_FOREACH(ct, &channel_tags, ct_link)
+    htsmsg_add_str(m, NULL, ct->ct_name);
+  return m;
 }
 
-/**
- *
- */
-channel_t *
-channel_find_by_name(const char *name, int create, int channel_number)
+static void
+channel_class_icon_notify ( void *obj )
 {
-  lock_assert(&global_lock);
-  channel_t *ch, skel;
+  channel_t *ch = obj;
+  if (ch->ch_icon)
+    imagecache_get_id(ch->ch_icon);
+}
 
-  if (name) {
-    skel.ch_name = (char *)name;
-    ch = RB_FIND(&channel_name_tree, &skel, ch_name_link, channelcmp);
-    if(ch != NULL || create == 0)
-      return ch;
+static const char *
+channel_class_get_title ( idnode_t *self )
+{
+  channel_t *ch = (channel_t*)self;
+  return ch->ch_name;
+}
+
+const idclass_t channel_class = {
+  .ic_class      = "service",
+  .ic_caption    = "Service",
+  .ic_save       = channel_class_save,
+  .ic_get_title  = channel_class_get_title,
+  .ic_properties = (const property_t[]){
+#if 0
+    {
+      .type     = PT_BOOL,
+      .id       = "enabled",
+      .name     = "Enabled",
+      .off      = offsetof(service_t, s_enabled),
+    },
+#endif
+    {
+      .type     = PT_STR,
+      .id       = "name",
+      .name     = "Name",
+      .off      = offsetof(channel_t, ch_name),
+    },
+    {
+      .type     = PT_INT,
+      .id       = "number",
+      .name     = "Number",
+      .off      = offsetof(channel_t, ch_number),
+    },
+    {
+      .type     = PT_STR,
+      .id       = "icon",
+      .name     = "Icon",
+      .off      = offsetof(channel_t, ch_icon),
+      .notify   = channel_class_icon_notify,
+    },
+    {
+      .type     = PT_INT,
+      .id       = "dvr_pre_time",
+      .name     = "DVR Pre", // TODO: better text?
+      .off      = offsetof(channel_t, ch_dvr_extra_time_pre),
+    },
+    {
+      .type     = PT_INT,
+      .id       = "dvr_pst_time",
+      .name     = "DVR Post", // TODO: better text?
+      .off      = offsetof(channel_t, ch_dvr_extra_time_post),
+    },
+    {
+      .type     = PT_STR,
+      .id       = "services",
+      .name     = "Services",
+      .get      = channel_class_services_get,
+      .set      = channel_class_services_set,
+      .list     = channel_class_services_enum,
+      .opts     = PO_MULTI
+    },
+    {
+      .type     = PT_STR,
+      .id       = "tags",
+      .name     = "Tags",
+      .get      = channel_class_tags_get,
+      .set      = channel_class_tags_set,
+      .list     = channel_class_tags_enum,
+    },
+    {}
   }
-  return channel_create2(name, channel_number);
-}
+};
 
+/* **************************************************************************
+ * Find
+ * *************************************************************************/
 
-/**
- *
- */
+// Note: since channel names are no longer unique this method will simply
+//       return the first entry encountered, so could be somewhat random
 channel_t *
-channel_find_by_identifier(int id)
+channel_find_by_name ( const char *name )
 {
-  channel_t skel, *ch;
-
-  lock_assert(&global_lock);
-
-  skel.ch_id = id;
-  ch = RB_FIND(&channel_identifier_tree, &skel, ch_identifier_link, chidcmp);
+  channel_t *ch;
+  CHANNEL_FOREACH(ch)
+    if (!strcmp(ch->ch_name ?: "", name))
+      break;
   return ch;
 }
 
-/**
- *
- */
-static void
-channel_load_one(htsmsg_t *c, int id)
+channel_t *
+channel_find_by_id ( uint32_t i )
 {
-  channel_t *ch;
-  const char *name = htsmsg_get_str(c, "name");
-  htsmsg_t *tags;
-  htsmsg_field_t *f;
-  channel_tag_t *ct;
-  char buf[32];
+  channel_t skel;
+  memcpy(skel.ch_id.in_uuid, &i, sizeof(i));
 
-  if(name == NULL)
-    return;
-
-  ch = calloc(1, sizeof(channel_t));
-  ch->ch_id = id;
-  if(RB_INSERT_SORTED(&channel_identifier_tree, ch, 
-                     ch_identifier_link, chidcmp)) {
-    /* ID collision, should not happen unless there is something
-       wrong in the setting storage */
-    free(ch);
-    return;
-  }
-
-  channel_set_name(ch, name);
-
-  epggrab_channel_add(ch);
-
-  tvh_str_update(&ch->ch_icon, htsmsg_get_str(c, "icon"));
-  imagecache_get_id(ch->ch_icon);
-
-  htsmsg_get_s32(c, "dvr_extra_time_pre",  &ch->ch_dvr_extra_time_pre);
-  htsmsg_get_s32(c, "dvr_extra_time_post", &ch->ch_dvr_extra_time_post);
-  htsmsg_get_s32(c, "channel_number", &ch->ch_number);
-
-  if((tags = htsmsg_get_list(c, "tags")) != NULL) {
-    HTSMSG_FOREACH(f, tags) {
-      if(f->hmf_type == HMF_S64) {
-       snprintf(buf, sizeof(buf), "%" PRId64 , f->hmf_s64);
-
-       if((ct = channel_tag_find(buf, 0)) != NULL) 
-         channel_tag_map(ch, ct, 1);
-      }
-    }
-  }
-
-  // TODO: load services
+  return RB_FIND(&channels, &skel, ch_link, ch_id_cmp);
 }
 
+/* **************************************************************************
+ * Property updating
+ * *************************************************************************/
 
-/**
- *
- */
-static void
-channels_load(void)
+int
+channel_set_services_by_list ( channel_t *ch, const char *svcs )
 {
-  htsmsg_t *l, *c;
-  htsmsg_field_t *f;
-
-  if((l = hts_settings_load("channels")) != NULL) {
-    HTSMSG_FOREACH(f, l) {
-      if((c = htsmsg_get_map_by_field(f)) == NULL)
-       continue;
-      channel_load_one(c, atoi(f->hmf_name));
+  int save = 0;
+  char *tmp, *ret, *tok;
+  service_t *svc;
+  channel_service_mapping_t *csm, *n;
+
+  /* Mark all for deletion */
+  LIST_FOREACH(csm, &ch->ch_services, csm_chn_link)
+    csm->csm_mark = 1;
+
+  /* Link */
+  tmp = strdup(svcs);
+  tok = strtok_r(tmp, ",", &ret);
+  while (tok) {
+    if ((svc = service_find(tok)))
+      save |= service_mapper_link(svc, ch);
+    tok = strtok_r(NULL, ",", &ret);
+  }
+  free(tmp);
+
+  /* Remove */
+  for (csm = LIST_FIRST(&ch->ch_services); csm != NULL; csm = n) {
+    n = LIST_NEXT(csm, csm_chn_link);
+    if (csm->csm_mark) {
+      LIST_REMOVE(csm, csm_chn_link);
+      LIST_REMOVE(csm, csm_svc_link);
+      free(csm);
+      save = 1;
     }
-    htsmsg_destroy(l);
   }
-}
-
-
-/**
- * Write out a config file for a channel
- */
-void
-channel_save(channel_t *ch)
-{
-  htsmsg_t *m = htsmsg_create_map();
-  htsmsg_t *tags;
-  channel_tag_mapping_t *ctm;
-
-  lock_assert(&global_lock);
 
-  htsmsg_add_str(m, "name", ch->ch_name);
-
-  if(ch->ch_icon != NULL)
-    htsmsg_add_str(m, "icon", ch->ch_icon);
-
-  tags = htsmsg_create_list();
-  LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link)
-    htsmsg_add_u32(tags, NULL, ctm->ctm_tag->ct_identifier);
-
-  htsmsg_add_msg(m, "tags", tags);
-
-  htsmsg_add_u32(m, "dvr_extra_time_pre",  ch->ch_dvr_extra_time_pre);
-  htsmsg_add_u32(m, "dvr_extra_time_post", ch->ch_dvr_extra_time_post);
-  htsmsg_add_s32(m, "channel_number", ch->ch_number);
-  
-  /* TODO: save services */
-
-  hts_settings_save(m, "channels/%d", ch->ch_id);
-  htsmsg_destroy(m);
+  return save;
 }
 
-/**
- * Rename a channel and all tied services
- */
 int
-channel_rename(channel_t *ch, const char *newname)
+channel_set_tags_by_list ( channel_t *ch, const char *tags )
 {
-  dvr_entry_t *de;
-
-  lock_assert(&global_lock);
+  return 0;
+}
 
-  if (!newname || !*newname) return 0;
+/* **************************************************************************
+ * Creation/Deletion
+ * *************************************************************************/
 
-  if(channel_find_by_name(newname, 0, 0))
-    return -1;
+channel_t *
+channel_create0
+  ( channel_t *ch, const idclass_t *idc, const char *uuid, htsmsg_t *conf,
+    const char *name )
+{
+  lock_assert(&global_lock);
 
-  tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" renamed to \"%s\"",
-        ch->ch_name, newname);
+  idnode_insert(&ch->ch_id, uuid, idc);
+  if (RB_INSERT_SORTED(&channels, ch, ch_link, ch_id_cmp)) {
+    tvherror("channel", "id collision!");
+    abort();
+  }
 
-  RB_REMOVE(&channel_name_tree, ch, ch_name_link);
-  channel_set_name(ch, newname);
-  epggrab_channel_mod(ch);
+  if (conf)
+    idnode_load(&ch->ch_id, conf);
 
-  LIST_FOREACH(de, &ch->ch_dvrs, de_channel_link) {
-    dvr_entry_save(de);
-    dvr_entry_notify(de);
+  /* Override the name */
+  if (name) {
+    free(ch->ch_name);
+    ch->ch_name = strdup(name);
   }
 
-  channel_save(ch);
-  htsp_channel_update(ch);
-  return 0;
+  return ch;
 }
 
-/**
- * Delete channel
- */
 void
-channel_delete(channel_t *ch)
+channel_delete ( channel_t *ch )
 {
-  channel_service_mapping_t *t;
   th_subscription_t *s;
   channel_tag_mapping_t *ctm;
+  channel_service_mapping_t *csm;
 
   lock_assert(&global_lock);
 
+  tvhinfo("channel", "%s - deleting", ch->ch_name);
+
+  /* Tags */
   while((ctm = LIST_FIRST(&ch->ch_ctms)) != NULL)
     channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_TAG);
 
-  tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" deleted",
-        ch->ch_name);
-
+  /* DVR */
   autorec_destroy_by_channel(ch);
-
   dvr_destroy_by_channel(ch);
 
-  while((t = LIST_FIRST(&ch->ch_services)) != NULL)
-    service_mapper_unlink(t->csm_svc, ch);
+  /* Services */
+  while((csm = LIST_FIRST(&ch->ch_services)) != NULL)
+    service_mapper_unlink(csm->csm_svc, ch);
 
+  /* Subscriptions */
   while((s = LIST_FIRST(&ch->ch_subscriptions)) != NULL) {
     LIST_REMOVE(s, ths_channel_link);
     s->ths_channel = NULL;
   }
 
+  /* EPG */
+#if 0
   epggrab_channel_rem(ch);
   epg_channel_unlink(ch);
+#endif
 
-  hts_settings_remove("channels/%d", ch->ch_id);
-
-  htsp_channel_delete(ch);
-
-  RB_REMOVE(&channel_name_tree, ch, ch_name_link);
-  RB_REMOVE(&channel_identifier_tree, ch, ch_identifier_link);
+  /* Settings */
+  hts_settings_remove("channel/%s", idnode_uuid_as_str(&ch->ch_id));
 
+  /* Free memory */
+  RB_REMOVE(&channels, ch, ch_link);
+  idnode_unlink(&ch->ch_id);
   free(ch->ch_name);
-  free(ch->ch_sname);
   free(ch->ch_icon);
-
-  channel_list_changed();
-  
   free(ch);
 }
 
-
-
-/**
- * Merge services from channel 'src' to channel 'dst'
- *
- * Then, destroy the 'src' channel
+/*
+ * Save
  */
 void
-channel_merge(channel_t *dst, channel_t *src)
+channel_save ( channel_t *ch )
 {
-  channel_service_mapping_t *t;
-
-  lock_assert(&global_lock);
-  
-  tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" merged into \"%s\"",
-        src->ch_name, dst->ch_name);
-
-  while((t = LIST_FIRST(&src->ch_services)) != NULL)
-    service_mapper_link(t->csm_svc, dst);
-
-  channel_delete(src);
+  htsmsg_t *c = htsmsg_create_map();
+  idnode_save(&ch->ch_id, c);
+  hts_settings_save(c, "channel/%s", idnode_uuid_as_str(&ch->ch_id));
+  htsmsg_destroy(c);
 }
 
 /**
  *
  */
 void
-channel_set_icon(channel_t *ch, const char *icon)
+channel_init ( void )
 {
-  lock_assert(&global_lock);
+  htsmsg_t *c, *e;
+  htsmsg_field_t *f;
+  RB_INIT(&channels);
+  
+  /* Tags */
+  channel_tag_init();
 
-  if(ch->ch_icon != NULL && !strcmp(ch->ch_icon, icon))
+  /* Channels */
+  if (!(c = hts_settings_load_r(1, "channel")))
     return;
 
-  free(ch->ch_icon);
-  ch->ch_icon = strdup(icon);
-  imagecache_get_id(icon);
-  channel_save(ch);
-  htsp_channel_update(ch);
-}
-
-/**
- *  Set the amount of minutes to start before / end after recording on a channel
- */
-void
-channel_set_epg_postpre_time(channel_t *ch, int pre, int mins)
-{
-  if (mins < -10000 || mins > 10000)
-    mins = 0;
-
-  lock_assert(&global_lock);
-
-  tvhlog(LOG_NOTICE, "channels", 
-        "Channel \"%s\" epg %s-time set to %d minutes",
-        ch->ch_name, pre ? "pre":"post", mins);
-
-  if (pre) {
-    if (ch->ch_dvr_extra_time_pre == mins)
-      return;
-    else
-      ch->ch_dvr_extra_time_pre = mins;
-  } else {
-    if (ch->ch_dvr_extra_time_post == mins)
-      return;
-    else
-       ch->ch_dvr_extra_time_post = mins;
+  HTSMSG_FOREACH(f, c) {
+    if (!(e = htsmsg_field_get_map(f))) continue;
+    (void)channel_create(f->hmf_name, e, NULL);
   }
-  channel_save(ch);
-  htsp_channel_update(ch);
+  htsmsg_destroy(c);
 }
 
-/**
- * Set the channel number
+/* ***
+ * Channel tags TODO
  */
-void
-channel_set_number(channel_t *ch, int number)
-{
-  if(ch->ch_number == number)
-    return;
-  ch->ch_number = number;
-  channel_save(ch);
-  htsp_channel_update(ch);
-}
-
-/**
- *
- */
-void
-channel_set_tags_from_list(channel_t *ch, const char *maplist)
-{
-  channel_tag_mapping_t *ctm, *n;
-  channel_tag_t *ct;
-  char buf[40];
-  int i, change = 0;
-
-  lock_assert(&global_lock);
-
-  LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link)
-    ctm->ctm_mark = 1; /* Mark for delete */
-
-  while(*maplist) {
-    for(i = 0; i < sizeof(buf) - 1; i++) {
-      buf[i] = *maplist;
-      if(buf[i] == 0)
-       break;
-
-      maplist++;
-      if(buf[i] == ',') {
-       break;
-      }
-    }
-
-    buf[i] = 0;
-    if((ct = channel_tag_find(buf, 0)) == NULL)
-      continue;
-
-    LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link)
-      if(ctm->ctm_tag == ct) {
-       ctm->ctm_mark = 0;
-       break;
-      }
-    
-    if(ctm == NULL) {
-      /* Need to create mapping */
-      change = 1;
-      channel_tag_map(ch, ct, 0);
-    }
-  }
-
-  for(ctm = LIST_FIRST(&ch->ch_ctms); ctm != NULL; ctm = n) {
-    n = LIST_NEXT(ctm, ctm_channel_link);
-    if(ctm->ctm_mark) {
-      change = 1;
-      channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_TAG |
-                                 CTM_DESTROY_UPDATE_CHANNEL);
-    }
-  }
-
-  if(change)
-    channel_save(ch);
-}
-
-
-
 
 /**
  *
@@ -883,17 +754,10 @@ channel_tag_find_by_identifier(uint32_t id) {
   return NULL;
 }
 
-
-/**
- *
- */
-void
-channels_init(void)
+static void
+channel_tag_init ( void )
 {
   TAILQ_INIT(&channel_tags);
-
   channeltags_dtable = dtable_create(&channel_tags_dtc, "channeltags", NULL);
   dtable_load(channeltags_dtable);
-
-  channels_load();
 }
index 8a8af9696096528ec146ea329e799eb45f817ed8..bb86f4d01ae1f2a93af5857c6d3d768801221939 100644 (file)
 #define CHANNELS_H
 
 #include "epg.h"
+#include "idnode.h"
+
+RB_HEAD(channel_tree, channel);
 
 LIST_HEAD(channel_tag_mapping_list, channel_tag_mapping);
 TAILQ_HEAD(channel_tag_queue, channel_tag);
 
 extern struct channel_tag_queue channel_tags;
+extern struct channel_tree      channels;
 
+#define CHANNEL_FOREACH(ch) RB_FOREACH(ch, &channels, ch_link)
 
 /*
  * Channel definition
  */ 
-typedef struct channel {
+typedef struct channel
+{
+  idnode_t ch_id;
+
+  RB_ENTRY(channel)   ch_link;
   
   int ch_refcount;
   int ch_zombie;
 
-  RB_ENTRY(channel) ch_name_link;
+  /* Channel info */
   char *ch_name;
-  char *ch_sname;
-
-  RB_ENTRY(channel) ch_identifier_link;
-  int ch_id;               
+  int   ch_number;
+  char *ch_icon;
+  struct channel_tag_mapping_list ch_ctms;
 
+  /* Service/subscriptions */
   LIST_HEAD(, channel_service_mapping) ch_services;
-  LIST_HEAD(, th_subscription) ch_subscriptions;
+  LIST_HEAD(, th_subscription)         ch_subscriptions;
 
   /* EPG fields */
   epg_broadcast_tree_t  ch_epg_schedule;
   epg_broadcast_t      *ch_epg_now;
   epg_broadcast_t      *ch_epg_next;
   gtimer_t              ch_epg_timer;
+  gtimer_t              ch_epg_timer_head;
+  gtimer_t              ch_epg_timer_current;
 
-  gtimer_t ch_epg_timer_head;
-  gtimer_t ch_epg_timer_current;
-  int ch_dvr_extra_time_pre;
-  int ch_dvr_extra_time_post;
-  int ch_number;  // User configurable number
-  char *ch_icon;
-
+  /* DVR */
+  int                   ch_dvr_extra_time_pre;
+  int                   ch_dvr_extra_time_post;
   struct dvr_entry_list ch_dvrs;
-  
   struct dvr_autorec_entry_list ch_autorecs;
 
-  struct channel_tag_mapping_list ch_ctms;
-
-
 } channel_t;
 
 
@@ -85,7 +88,6 @@ typedef struct channel_tag {
   struct dvr_autorec_entry_list ct_autorecs;
 } channel_tag_t;
 
-
 /**
  * Channel tag mapping
  */
@@ -100,39 +102,41 @@ typedef struct channel_tag_mapping {
 
 } channel_tag_mapping_t;
 
+/*
+ * Service mappings
+ */
 typedef struct channel_service_mapping {
   LIST_ENTRY(channel_service_mapping) csm_chn_link;
   LIST_ENTRY(channel_service_mapping) csm_svc_link;
   
   struct channel *csm_chn;
   struct service *csm_svc;
-} channel_service_mapping_t;
-
-void channels_init(void);
-
-channel_t *channel_create(const char *name);
 
-channel_t *channel_find_by_name(const char *name, int create, int number);
-
-channel_t *channel_find_by_identifier(int id);
+  int csm_mark;
+} channel_service_mapping_t;
 
-void channel_set_teletext_rundown(channel_t *ch, int v);
+extern const idclass_t channel_class;
 
-void channel_settings_write(channel_t *ch);
+void channel_init(void);
 
-int channel_rename(channel_t *ch, const char *newname);
+channel_t *channel_create0
+  (channel_t *ch, const idclass_t *idc, const char *uuid, htsmsg_t *conf,
+   const char *name);
+#define channel_create(u, c, n)\
+  channel_create0(calloc(1, sizeof(channel_t)), &channel_class, u, c, n)
 
 void channel_delete(channel_t *ch);
 
-void channel_merge(channel_t *dst, channel_t *src);
-
-void channel_set_epg_postpre_time(channel_t *ch, int pre, int mins);
+channel_t *channel_find_by_name(const char *name);
+#define channel_find_by_uuid(u)\
+  (channel_t*)idnode_find(NULL, &channel_class)
 
-void channel_set_number(channel_t *ch, int number);
+channel_t *channel_find_by_id(uint32_t id);
 
-void channel_set_icon(channel_t *ch, const char *icon);
+#define channel_find channel_find_by_uuid
 
-void channel_set_tags_from_list(channel_t *ch, const char *maplist);
+int channel_set_tags_by_list ( channel_t *ch, const char *tags );
+int channel_set_services_by_list ( channel_t *ch, const char *svcs );
 
 channel_tag_t *channel_tag_find_by_name(const char *name, int create);
 
@@ -142,4 +146,8 @@ int channel_tag_map(channel_t *ch, channel_tag_t *ct, int check);
 
 void channel_save(channel_t *ch);
 
+#define channel_get_uuid(ch) idnode_uuid_as_str(&ch->ch_id)
+
+#define channel_get_id(ch)   idnode_get_short_uuid((&ch->ch_id))
+
 #endif /* CHANNELS_H */
index 57609ef83950f63f5cd7ac2d0b4868b225974381..f3384f99bb6df2e47360fea295d6e2c275685767 100644 (file)
@@ -362,7 +362,7 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values,
       LIST_REMOVE(dae, dae_channel_link);
       dae->dae_channel = NULL;
     }
-    if((ch = channel_find_by_name(s, 0, 0)) != NULL) {
+    if((ch = channel_find(s)) != NULL) {
       LIST_INSERT_HEAD(&ch->ch_autorecs, dae, dae_channel_link);
       dae->dae_channel = ch;
     }
@@ -553,7 +553,7 @@ dvr_autorec_add(const char *config_name,
                const char *creator, const char *comment)
 {
   channel_t *ch = NULL;
-  if(channel != NULL) ch = channel_find_by_name(channel, 0, 0);
+  if(channel != NULL) ch = channel_find(channel);
   _dvr_autorec_add(config_name, title, ch, tag, content_type,
                    NULL, NULL, NULL, 0, NULL, creator, comment);
 }
@@ -624,7 +624,7 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge)
   if (purge)
     dvr_autorec_purge_spawns(dae);
 
-  RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
+  CHANNEL_FOREACH(ch) {
     RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
       if(autorec_cmp(dae, e))
              dvr_entry_create_by_autorec(e, dae);
index d6a1562ca7b8a8dfccdff02850d477973814588d..48818faab82fe640efc77d2f62e071eae635f2bf 100644 (file)
@@ -519,7 +519,7 @@ static void
 dvr_db_load_one(htsmsg_t *c, int id)
 {
   dvr_entry_t *de;
-  const char *chname, *s, *creator;
+  const char *chuuid, *chname, *s, *creator;
   channel_t *ch;
   uint32_t start, stop, bcid, u32;
   int d;
@@ -531,9 +531,15 @@ dvr_db_load_one(htsmsg_t *c, int id)
   if(htsmsg_get_u32(c, "stop", &stop))
     return;
 
-  if((chname = htsmsg_get_str(c, "channel")) == NULL)
-    return;
-  ch = channel_find_by_name(chname, 0, 0);
+  chname = htsmsg_get_str(c, "channel_name");
+  chuuid = htsmsg_get_str(c, "channel");
+  ch     = chuuid ? channel_find(chuuid) : NULL;
+
+  /* Backwards compat */
+  if (!ch && !chname) {
+    chname = chuuid;
+    ch     = channel_find_by_name(chname);
+  }
     
   s = htsmsg_get_str(c, "config_name");
   cfg = dvr_config_find_by_name_default(s);
@@ -648,6 +654,8 @@ dvr_entry_save(dvr_entry_t *de)
 
   lock_assert(&global_lock);
 
+  if (de->de_channel)
+    htsmsg_add_str(m, "channel", channel_get_uuid(de->de_channel));
   htsmsg_add_str(m, "channel", DVR_CH_NAME(de));
   htsmsg_add_u32(m, "start", de->de_start);
   htsmsg_add_u32(m, "stop", de->de_stop);
index e031ae07972423b08a6b4c42887e47ee4867338c..141d040ed28abef3f91ba4fad007808035d3e0af 100644 (file)
--- a/src/epg.c
+++ b/src/epg.c
@@ -1721,8 +1721,8 @@ epg_episode_t *epg_broadcast_get_episode
   if (!ebc) return NULL;
   if (ebc->episode) return ebc->episode;
   if (!create) return NULL;
-  snprintf(uri, sizeof(uri)-1, "tvh://channel-%d/bcast-%u/episode",
-           ebc->channel->ch_id, ebc->id);
+  snprintf(uri, sizeof(uri)-1, "tvh://channel-%s/bcast-%u/episode",
+           idnode_uuid_as_str(&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;
@@ -1756,7 +1756,7 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
   htsmsg_add_s64(m, "stop", broadcast->stop);
   htsmsg_add_str(m, "episode", broadcast->episode->uri);
   if (broadcast->channel)
-    htsmsg_add_u32(m, "channel", broadcast->channel->ch_id);
+    htsmsg_add_str(m, "channel", channel_get_uuid(broadcast->channel));
   if (broadcast->dvb_eid)
     htsmsg_add_u32(m, "dvb_eid", broadcast->dvb_eid);
   if (broadcast->is_widescreen)
@@ -1796,7 +1796,7 @@ epg_broadcast_t *epg_broadcast_deserialize
   epg_serieslink_t *esl;
   lang_str_t *ls;
   const char *str;
-  uint32_t chid, eid, u32;
+  uint32_t eid, u32;
   int64_t start, stop;
 
   if ( htsmsg_get_s64(m, "start", &start) ) return NULL;
@@ -1818,9 +1818,8 @@ epg_broadcast_t *epg_broadcast_deserialize
   }
 
   /* Get channel */
-  if ( !htsmsg_get_u32(m, "channel", &chid) ) {
-    ch  = channel_find_by_identifier(chid);
-  }
+  if ((str = htsmsg_get_str(m, "channel")))
+    ch = channel_find(str);
   if (!ch) return NULL;
 
   /* Create */
@@ -2254,9 +2253,11 @@ void epg_query0
 
   /* All channels */
   } else {
+#if TODO
     RB_FOREACH(channel, &channel_name_tree, ch_name_link) {
       _eqr_add_channel(eqr, channel, genre, preg, now, lang);
     }
+#endif
   }
   if (preg) regfree(preg);
 
@@ -2266,8 +2267,8 @@ void epg_query0
 void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
               epg_genre_t *genre, const char *title, const char *lang)
 {
-  channel_t     *ch = channel ? channel_find_by_name(channel, 0, 0) : NULL;
-  channel_tag_t *ct = tag     ? channel_tag_find_by_name(tag, 0)    : NULL;
+  channel_t     *ch = channel ? channel_find(channel)    : NULL;
+  channel_tag_t *ct = tag     ? channel_tag_find_by_name(tag, 0) : NULL;
   epg_query0(eqr, ch, ct, genre, title, lang);
 }
 
index ebc1513b96287ce17ea853974c51e9836506fa9a..a45d9e926dd763f2a348090ccc3cc661570d33d4 100644 (file)
@@ -44,6 +44,7 @@ extern epg_object_tree_t epg_serieslinks;
 /*
  * Use for v1 databases
  */
+#if DEPRECATED
 static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats )
 {
   channel_t *ch;
@@ -94,6 +95,7 @@ static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats )
   /* Set episode */
   save |= epg_broadcast_set_episode(ebc, ee, NULL);
 }
+#endif
 
 /*
  * Process v2 data
@@ -207,8 +209,7 @@ void epg_init ( void )
       case 2:
         _epgdb_v2_process(m, &stats);
         break;
-      default: /* v0/1 */
-        _epgdb_v1_process(m, &stats);
+      default:
         break;
     }
 
@@ -267,8 +268,10 @@ void epg_save ( void *p )
 {
   int fd;
   epg_object_t *eo;
+#if 0
   epg_broadcast_t *ebc;
   channel_t *ch;
+#endif
   epggrab_stats_t stats;
   extern gtimer_t epggrab_save_timer;
 
@@ -300,12 +303,14 @@ void epg_save ( void *p )
     stats.seasons.total++;
   }
   if ( _epg_write_sect(fd, "broadcasts") ) return;
+#if 0
   RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
     RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
       if (_epg_write(fd, epg_broadcast_serialize(ebc))) return;
       stats.broadcasts.total++;
     }
   }
+#endif
 
   /* Stats */
   tvhlog(LOG_INFO, "epgdb", "saved");
index 3fe0381c11888798f2594bda409e1569a51c4923..87cb6904ffa09f4b6a995c5e90cff60cf91e7782 100644 (file)
@@ -57,12 +57,14 @@ void epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
   ecl = calloc(1, sizeof(epggrab_channel_link_t));
   ecl->channel = ch;
   LIST_INSERT_HEAD(&ec->channels, ecl, link);
+#if 0
   if (ec->name && epggrab_channel_rename)
     channel_rename(ch, ec->name);
   if (ec->icon && epggrab_channel_reicon)
     channel_set_icon(ch, ec->icon);
   if (ec->number>0 && epggrab_channel_renumber)
     channel_set_number(ch, ec->number);
+#endif
 
   /* Save */
   if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec);
@@ -82,6 +84,7 @@ 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;
+#if 0
   epggrab_channel_link_t *ecl;
   if (!ec || !name) return 0;
   if (!ec->name || strcmp(ec->name, name)) {
@@ -92,6 +95,7 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
         channel_rename(ecl->channel, name);
     save = 1;
   }
+#endif
   return save;
 }
 
@@ -99,6 +103,7 @@ 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;
+#if 0
   epggrab_channel_link_t *ecl;
   if (!ec->icon || strcmp(ec->icon, icon) ) {
   if (!ec | !icon) return 0;
@@ -109,6 +114,7 @@ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
         channel_set_icon(ecl->channel, icon);
     save = 1;
   }
+#endif
   return save;
 }
 
@@ -116,6 +122,7 @@ 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;
+#if 0
   epggrab_channel_link_t *ecl;
   if (!ec || (number <= 0)) return 0;
   if (ec->number != number) {
@@ -125,12 +132,14 @@ int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
         channel_set_number(ecl->channel, number);
     save = 1;
   }
+#endif
   return save;
 }
 
 /* Channel settings updated */
 void epggrab_channel_updated ( epggrab_channel_t *ec )
 {
+#if 0
   channel_t *ch;
   if (!ec) return;
 
@@ -141,6 +150,7 @@ void epggrab_channel_updated ( epggrab_channel_t *ec )
 
   /* Save */
   if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec);
+#endif
 }
 
 /* ID comparison */
index 788dd8abf93c4c0beef7fd2069be0cf71f146f50..a2bd9810f94f463a2a7dc1c487f63a429873f865 100644 (file)
@@ -154,7 +154,7 @@ void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch )
     htsmsg_add_str(m, "icon", ch->icon);
   LIST_FOREACH(ecl, &ch->channels, link) {
     if (!a) a = htsmsg_create_list();
-    htsmsg_add_u32(a, NULL, ecl->channel->ch_id);
+    htsmsg_add_u32(a, NULL, channel_get_id(ecl->channel));
   }
   if (a) htsmsg_add_msg(m, "channels", a);
   if (ch->number)
@@ -214,7 +214,7 @@ static void _epggrab_module_channel_load
     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))) {
+      if ((ch  = channel_find_by_id((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);
@@ -223,7 +223,7 @@ static void _epggrab_module_channel_load
 
   /* Compat with older 3.1 code */
   } else if (!htsmsg_get_u32(m, "channel", &u32)) {
-    if ((ch = channel_find_by_identifier(u32))) {
+    if ((ch = channel_find_by_id(u32))) {
       epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t));
       ecl->channel = ch;
       LIST_INSERT_HEAD(&egc->channels, ecl, link);
index 5d5d682ec448d405808d022ecc342570164214d2..70c32e71728ae82c8f71d761b04253c44028bf4c 100644 (file)
@@ -463,7 +463,7 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp)
   htsmsg_t *tags = htsmsg_create_list();
   htsmsg_t *services = htsmsg_create_list();
 
-  htsmsg_add_u32(out, "channelId", ch->ch_id);
+  htsmsg_add_u32(out, "channelId", channel_get_id(ch));
   htsmsg_add_u32(out, "channelNumber", ch->ch_number);
 
   htsmsg_add_str(out, "channelName", ch->ch_name);
@@ -545,7 +545,7 @@ htsp_build_tag(channel_tag_t *ct, const char *method, int include_channels)
 
   if(members != NULL) {
     LIST_FOREACH(ctm, &ct->ct_ctms, ctm_tag_link)
-      htsmsg_add_u32(members, NULL, ctm->ctm_channel->ch_id);
+      htsmsg_add_u32(members, NULL, channel_get_id(ctm->ctm_channel));
     htsmsg_add_msg(out, "members", members);
   }
 
@@ -566,7 +566,7 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
 
   htsmsg_add_u32(out, "id", de->de_id);
   if (de->de_channel)
-    htsmsg_add_u32(out, "channel", de->de_channel->ch_id);
+    htsmsg_add_u32(out, "channel", channel_get_id(de->de_channel));
 
   htsmsg_add_s64(out, "start", de->de_start);
   htsmsg_add_s64(out, "stop", de->de_stop);
@@ -647,7 +647,7 @@ htsp_build_event
     htsmsg_add_str(out, "method", method);
 
   htsmsg_add_u32(out, "eventId", e->id);
-  htsmsg_add_u32(out, "channelId", e->channel->ch_id);
+  htsmsg_add_u32(out, "channelId", channel_get_id(e->channel));
   htsmsg_add_s64(out, "start", e->start);
   htsmsg_add_s64(out, "stop", e->stop);
   if ((str = epg_broadcast_get_title(e, lang)))
@@ -846,7 +846,7 @@ htsp_method_async(htsp_connection_t *htsp, htsmsg_t *in)
       htsp_send_message(htsp, htsp_build_tag(ct, "tagAdd", 0), NULL);
   
   /* Send all channels */
-  RB_FOREACH(ch, &channel_name_tree, ch_name_link)
+  CHANNEL_FOREACH(ch)
     htsp_send_message(htsp, htsp_build_channel(ch, "channelAdd", htsp), NULL);
     
   /* Send all enabled and external tags (now with channel mappings) */
@@ -860,7 +860,7 @@ htsp_method_async(htsp_connection_t *htsp, htsmsg_t *in)
 
   /* Send EPG updates */
   if (epg) {
-    RB_FOREACH(ch, &channel_name_tree, ch_name_link)
+    CHANNEL_FOREACH(ch)
       RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
         if (epgMaxTime && ebc->start > epgMaxTime) break;
         htsmsg_t *e = htsp_build_event(ebc, "eventAdd", lang, lastUpdate, htsp);
@@ -915,7 +915,7 @@ htsp_method_getEvents(htsp_connection_t *htsp, htsmsg_t *in)
 
   /* Optional fields */
   if (!htsmsg_get_u32(in, "channelId", &u32))
-    if (!(ch = channel_find_by_identifier(u32)))
+    if (!(ch = channel_find_by_id(u32)))
       return htsp_error("Channel does not exist");
   if (!htsmsg_get_u32(in, "eventId", &u32))
     if (!(e = epg_broadcast_find_by_id(u32, ch)))
@@ -944,7 +944,7 @@ htsp_method_getEvents(htsp_connection_t *htsp, htsmsg_t *in)
   /* All channels */
   } else {
     events = htsmsg_create_list();
-    RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
+    CHANNEL_FOREACH(ch) {
       int num = numFollowing;
       RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
         if (maxTime && e->start > maxTime) break;
@@ -984,7 +984,7 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
   
   /* Optional */
   if(!(htsmsg_get_u32(in, "channelId", &u32)))
-    if (!(ch = channel_find_by_identifier(u32)))
+    if (!(ch = channel_find_by_id(u32)))
       return htsp_error("Channel does not exist");
   if(!(htsmsg_get_u32(in, "tagId", &u32)))
     if (!(ct = channel_tag_find_by_identifier(u32)))
@@ -1072,7 +1072,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
   if(htsmsg_get_s64(in, "stopExtra", &stop_extra))
     stop_extra  = 0;
   if(!htsmsg_get_u32(in, "channelId", &u32))
-    ch = channel_find_by_identifier(u32);
+    ch = channel_find_by_id(u32);
   if(!htsmsg_get_u32(in, "eventId", &eventid))
     e = epg_broadcast_find_by_id(eventid, ch);
   if(htsmsg_get_u32(in, "priority", &priority))
@@ -1256,18 +1256,18 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in)
 #if ENABLE_TIMESHIFT
   uint32_t timeshiftPeriod = 0;
 #endif
+  const char *str;
   channel_t *ch;
   htsp_subscription_t *hs;
-  const char *str;
   if(htsmsg_get_u32(in, "subscriptionId", &sid))
     return htsp_error("Missing argument 'subscriptionId'");
 
   if(!htsmsg_get_u32(in, "channelId", &chid)) {
 
-    if((ch = channel_find_by_identifier(chid)) == NULL)
+    if((ch = channel_find_by_id(chid)) == NULL)
       return htsp_error("Requested channel does not exist");
   } else if((str = htsmsg_get_str(in, "channelName")) != NULL) {
-    if((ch = channel_find_by_name(str, 0, 0)) == NULL)
+    if((ch = channel_find_by_name(str)) == NULL)
       return htsp_error("Requested channel does not exist");
 
   } else {
@@ -2043,7 +2043,7 @@ htsp_channel_update_current(channel_t *ch)
 
   m = htsmsg_create_map();
   htsmsg_add_str(m, "method", "channelUpdate");
-  htsmsg_add_u32(m, "channelId", ch->ch_id);
+  htsmsg_add_u32(m, "channelId", channel_get_id(ch));
 
   now  = ch->ch_epg_now;
   next = ch->ch_epg_next;
@@ -2086,7 +2086,7 @@ void
 htsp_channel_delete(channel_t *ch)
 {
   htsmsg_t *m = htsmsg_create_map();
-  htsmsg_add_u32(m, "channelId", ch->ch_id);
+  htsmsg_add_u32(m, "channelId", channel_get_id(ch));
   htsmsg_add_str(m, "method", "channelDelete");
   htsp_async_send(m, HTSP_ASYNC_ON);
 }
index c8e8c42295bab4eb26712c894706ee95fd48dbd1..ff64bd690605f28c14b1ea515b48d215be3039e0 100644 (file)
@@ -206,6 +206,14 @@ idnode_delete(idnode_t *in)
  * Info
  * *************************************************************************/
 
+uint32_t
+idnode_get_short_uuid (const idnode_t *in)
+{
+  uint32_t u32;
+  memcpy(&u32, in->in_uuid, sizeof(u32));
+  return u32;
+}
+
 /**
  *
  */
index b90ada989ed01fbaed5ab24da1055575576a1554..f456cab2fa940a18abfb32b920527887d08359ec 100644 (file)
@@ -110,6 +110,7 @@ void idnode_init(void);
 int  idnode_insert(idnode_t *in, const char *uuid, const idclass_t *idc);
 void idnode_unlink(idnode_t *in);
 
+uint32_t      idnode_get_short_uuid (const idnode_t *in);
 const char   *idnode_uuid_as_str  (const idnode_t *in);
 idnode_set_t *idnode_get_childs   (idnode_t *in);
 const char   *idnode_get_title    (idnode_t *in);
index ecd7d2a84ffea9a5e1f81c1996f070ed0d7ff77f..35ea683c1ff26f4e913d7ffe6df563e58b894b01 100644 (file)
@@ -49,7 +49,7 @@ tsfile_network_create_service
   if (s) {
     char buf[128];
     sprintf(buf, "channel-%d", t);
-    channel_t *c = channel_find_by_name(buf, 1, t);
+    channel_t *c = channel_create(NULL, NULL, buf);
     service_mapper_link((service_t*)s, c);
     t++;
   }
index c87830bf207274385b98769db15a395d9c035ea6..2277c1e509d42b158ff0f11a04a311e0b38b7114 100644 (file)
@@ -701,7 +701,21 @@ main(int argc, char **argv)
 
   service_init();
 
-  channels_init();
+#if ENABLE_TSFILE
+  if(opt_tsfile.num) {
+    tsfile_init(opt_tsfile_tuner ?: opt_tsfile.num);
+    for (i = 0; i < opt_tsfile.num; i++)
+      tsfile_add_file(opt_tsfile.str[i]);
+  }
+#endif
+#if ENABLE_IPTV
+  iptv_init();
+#endif
+#if ENABLE_LINUXDVB
+  linuxdvb_init(adapter_mask);
+#endif
+
+  channel_init();
 
   subscription_init();
 
@@ -726,19 +740,6 @@ main(int argc, char **argv)
 
   htsp_init(opt_bindaddr);
 
-#if ENABLE_TSFILE
-  if(opt_tsfile.num) {
-    tsfile_init(opt_tsfile_tuner ?: opt_tsfile.num);
-    for (i = 0; i < opt_tsfile.num; i++)
-      tsfile_add_file(opt_tsfile.str[i]);
-  }
-#endif
-#if ENABLE_IPTV
-  iptv_init();
-#endif
-#if ENABLE_LINUXDVB
-  linuxdvb_init(adapter_mask);
-#endif
 
   if(opt_subscribe != NULL)
     subscription_dummy_join(opt_subscribe, 1);
index 4488a2e6effdaff3cc283b29c40e6c2ab6ce5880..0a2fd4ed6623c7534a3fefabcea6e4512d99f3ca 100644 (file)
@@ -246,6 +246,8 @@ prop_serialize(void *obj, const property_t *pl, htsmsg_t *msg, int optmask, htsm
       htsmsg_add_u32(m, "nosave", 1);
     if (pl->opts & PO_WRONCE)
       htsmsg_add_u32(m, "wronce", 1);
+    if (pl->opts & PO_MULTI)
+      htsmsg_add_u32(m, "multi",  1);
 
     /* Enum list */
     if (pl->list)
index 0e1f5256e3ca260aa23f0aa2d3b4df7310ff6e03..f08d0855eeb02a4de0310596d4e4a26bc822fc4b 100644 (file)
@@ -43,6 +43,7 @@ typedef enum {
 #define PO_RDONLY 0x01  // Property is read-only 
 #define PO_NOSAVE 0x02  // Property is transient (not saved)
 #define PO_WRONCE 0x04  // Property is write-once (i.e. on creation)
+#define PO_MULTI  0x08  // Multi-select
 
 /*
  * Property definition
index 43277ecfc24e6192ca6d054c7c1c5ea8522e9abe..61f6e1eb8505832210d8081f461c01fbfd40ab7a 100644 (file)
@@ -49,24 +49,101 @@ static void service_class_save(struct idnode *self);
 
 struct service_queue service_all;
 
-#if 0
-static const void *service_class_channel_get(void *obj);
-static int service_class_channel_set(void *obj, const void *str);
-static htsmsg_t *service_class_channel_enum(void *p)
+static const void *
+service_class_channel_get ( void *obj )
 {
+  service_t *svc = obj;
+  channel_service_mapping_t *csm;
+  static char buf[2048], *s;
+  // TODO: make this dynamic length
+  int first = 1;
+
+  *buf = 0;
+  LIST_FOREACH(csm, &svc->s_channels, csm_chn_link) {
+    if (!first)
+      strcat(buf, ",");
+    strcat(buf, idnode_uuid_as_str(&csm->csm_chn->ch_id));
+    first = 0;
+  }
+  s = first ? NULL : buf;
+  
+  return &s;
+}
+
+static int
+service_class_channel_set
+  ( void *obj, const void *str )
+{
+  int save = 0;
+  service_t *svc = obj;
+  char *tmp, *tok, *sptr;
   channel_t *ch;
-  htsmsg_t *list = htsmsg_create_list();
-  RB_FOREACH(ch, &channel_name_tree, ch_name_link)
-    if (ch->ch_name)
-      htsmsg_add_str(list, NULL, ch->ch_name);
-  return list;
+  channel_service_mapping_t *csm, *n;
+
+  /* Mark all for deletion */
+  LIST_FOREACH(csm, &svc->s_channels, csm_chn_link)
+    csm->csm_mark = 1;
+
+  /* Make new links */
+  tmp = strdup(str);
+  tok = strtok_r(tmp, ",", &sptr);
+  while (tok) {
+    ch = channel_find(tok);
+    if (ch) {
+      save |= service_mapper_link(svc, ch);
+    }
+    tok = strtok_r(NULL, ",", &sptr);
+  }
+
+  /* Delete unlinked */
+  for (csm = LIST_FIRST(&svc->s_channels); csm != NULL; csm = n ) {
+    n = LIST_NEXT(csm, csm_chn_link);
+    if (csm->csm_mark) {
+      save = 1;
+      LIST_REMOVE(csm, csm_chn_link);
+      LIST_REMOVE(csm, csm_svc_link);
+      free(csm);
+    }
+  }
+    
+  return save;
+}
+
+static htsmsg_t *
+service_class_channel_enum
+  ( void *obj )
+{
+  htsmsg_t *p, *m = htsmsg_create_map();
+  htsmsg_add_str(m, "type",  "api");
+  htsmsg_add_str(m, "uri",   "channel/list");
+  htsmsg_add_str(m, "event", "channel");
+  p = htsmsg_create_map();
+  htsmsg_add_u32(p, "enum", 1);
+  htsmsg_add_msg(m, "params", p);
+  return m;
+}
+
+static const char *
+service_class_get_title ( idnode_t *self )
+{
+  static char *ret = NULL;
+  service_t *s = (service_t*)self;
+  if (ret) {
+    free(ret);
+    ret = NULL;
+  }
+  pthread_mutex_lock(&s->s_stream_mutex); 
+  if (s->s_nicename)
+    ret = strdup(s->s_nicename);
+  pthread_mutex_unlock(&s->s_stream_mutex);
+  return ret;
 }
-#endif
 
 const idclass_t service_class = {
   .ic_class      = "service",
   .ic_caption    = "Service",
   .ic_save       = service_class_save,
+  .ic_get_title  = service_class_get_title,
   .ic_properties = (const property_t[]){
     {
       .type     = PT_BOOL,
@@ -74,16 +151,15 @@ const idclass_t service_class = {
       .name     = "Enabled",
       .off      = offsetof(service_t, s_enabled),
     },
-#if 0
     {
       .type     = PT_STR,
       .id       = "channel",
       .name     = "Channel",
       .get      = service_class_channel_get,
       .set      = service_class_channel_set,
-      .list     = service_class_channel_enum
+      .list     = service_class_channel_enum,
+      .opts     = PO_MULTI | PO_NOSAVE
     },
-#endif
     {}
   }
 };
@@ -451,7 +527,7 @@ service_create0
  * Find a service based on the given identifier
  */
 service_t *
-service_find_by_identifier(const char *identifier)
+service_find(const char *identifier)
 {
   return idnode_find(identifier, &service_class);
 }
@@ -650,15 +726,18 @@ service_is_hdtv(service_t *t)
 int
 service_is_radio(service_t *t)
 {
+  int ret = 0;
   if (t->s_servicetype == ST_RADIO)
     return 1;
   else if (t->s_servicetype == ST_NONE) {
     elementary_stream_t *st;
     TAILQ_FOREACH(st, &t->s_components, es_link)
-      if (SCT_ISAUDIO(st->es_type))
-        return 1;
+      if (SCT_ISVIDEO(st->es_type))
+        return 0;
+      else if (SCT_ISAUDIO(st->es_type))
+        ret = 1;
   }
-  return 0;
+  return ret;
 }
 
 /**
index c38491f4f3e0eb24c508a475fe55894f34e13a9d..62f6f593bdadef9fa8ce74d41b0840672dfa88a6 100644 (file)
@@ -27,6 +27,8 @@ extern const idclass_t service_class;
 
 extern struct service_queue service_all;
 
+struct channel;
+
 /**
  * Stream, one media component for a service.
  */
@@ -232,6 +234,7 @@ typedef struct service {
    */
   enum {
     ST_NONE,
+    ST_OTHER,
     ST_SDTV,
     ST_HDTV,
     ST_RADIO
@@ -433,7 +436,8 @@ void service_unref(service_t *t);
 
 void service_ref(service_t *t);
 
-service_t *service_find_by_identifier(const char *identifier);
+service_t *service_find(const char *identifier);
+#define service_find_by_identifier service_find
 
 service_instance_t *service_find_instance(struct service *s,
                                           struct channel *ch,
@@ -455,6 +459,7 @@ const char *service_servicetype_txt(service_t *t);
 int service_is_sdtv(service_t *t);
 int service_is_hdtv(service_t *t);
 int service_is_radio(service_t *t);
+int service_is_other(service_t *t);
 #define service_is_tv(s) (service_is_hdtv(s) || service_is_sdtv(s))
 
 int service_is_encrypted ( service_t *t );
index f4292466299834d68001299be15eed8f8ffcc436..00974bc35c8dfa0a069548f9c906a7eb5e08dc35 100644 (file)
@@ -141,15 +141,17 @@ service_mapper_remove ( service_t *s )
 /*
  * Link service and channel
  */
-void
+int
 service_mapper_link ( service_t *s, channel_t *c )
 {
   channel_service_mapping_t *csm;
 
   /* Already linked */
   LIST_FOREACH(csm, &s->s_channels, csm_chn_link)
-    if (csm->csm_chn == c)
-      return;
+    if (csm->csm_chn == c) {
+      csm->csm_mark = 0;
+      return 0;
+    }
 
   /* Link */
   csm = calloc(1, sizeof(channel_service_mapping_t));
@@ -157,6 +159,7 @@ service_mapper_link ( service_t *s, channel_t *c )
   csm->csm_svc = s;
   LIST_INSERT_HEAD(&s->s_channels, csm, csm_svc_link);
   LIST_INSERT_HEAD(&c->ch_services, csm, csm_chn_link);
+  return 1;
 }
 
 void
@@ -182,7 +185,7 @@ void
 service_mapper_process ( service_t *s )
 {
   int num;
-  channel_t *chn;
+  channel_t *chn = NULL;
 
   /* Ignore */
   if (s->s_status == SERVICE_ZOMBIE)
@@ -194,10 +197,12 @@ service_mapper_process ( service_t *s )
 
   /* Find existing channel */
   num = s->s_channel_number(s);
+#if 0
   if (service_mapper_conf.merge_same_name)
-    chn = channel_find_by_name(s->s_channel_name(s), 1, num);
-  else
-    chn = channel_create(s->s_channel_name(s));
+    chn = channel_find_by_name(s->s_channel_name(s));
+#endif
+  if (!chn)
+    chn = channel_create(NULL, NULL, s->s_channel_name(s));
     
   /* Map */
   if (chn) {
index 417ad7df89c83afa8c773e779f922c70a14d32dd..e30a5be1c0a86a635533ef85945b5b3fb0d3c7ce 100644 (file)
@@ -42,7 +42,7 @@ void service_mapper_remove ( struct service *t );
 int  service_mapper_qlen   ( void );
 
 // Link service to channel
-void service_mapper_link   ( struct service *s, struct channel *c );
+int  service_mapper_link   ( struct service *s, struct channel *c );
 
 // Unlink service from channel
 void service_mapper_unlink ( struct service *s, struct channel *c );
index 1dcc6bbea9f0be408fa234edffc40a87b8ebcaa1..1fbba6038cadc7a9596459aeac09cb02a9ba4a49 100644 (file)
@@ -147,9 +147,6 @@ void gtimer_disarm(gtimer_t *gti);
  * List / Queue header declarations
  */
 LIST_HEAD(th_subscription_list, th_subscription);
-RB_HEAD(channel_tree, channel);
-TAILQ_HEAD(channel_queue, channel);
-LIST_HEAD(channel_list, channel);
 LIST_HEAD(dvr_config_list, dvr_config);
 LIST_HEAD(dvr_entry_list, dvr_entry);
 TAILQ_HEAD(ref_update_queue, ref_update);
@@ -481,7 +478,6 @@ int rate_to_sri(int rate);
 
 extern time_t dispatch_clock;
 extern struct service_list all_transports;
-extern struct channel_tree channel_name_tree;
 
 extern void scopedunlock(pthread_mutex_t **mtxp);
 
index d014dff05adec79e719d3ef2810efc7d8adcb4e7..8f1a2327b1de5b247b8a4d508d4a7fa0b7cb07a4 100644 (file)
@@ -37,7 +37,6 @@
 #include "channels.h"
 
 #include "dvr/dvr.h"
-#include "service_mapper.h"
 #include "epggrab.h"
 #include "epg.h"
 #include "muxer.h"
@@ -318,221 +317,6 @@ extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
   return 0;
 }
 
-
-/**
- *
- */
-static void
-extjs_channels_delete(htsmsg_t *in)
-{
-  htsmsg_field_t *f;
-  channel_t *ch;
-
-  TAILQ_FOREACH(f, &in->hm_fields, hmf_link)
-    if(f->hmf_type == HMF_S64 &&
-       (ch = channel_find_by_identifier(f->hmf_s64)) != NULL)
-      channel_delete(ch);
-}
-
-/**
- *
- */
-static void
-extjs_channels_update(htsmsg_t *in)
-{
-  htsmsg_field_t *f;
-  channel_t *ch;
-  htsmsg_t *c;
-  uint32_t id;
-  const char *s;
-
-  TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
-    if((c = htsmsg_get_map_by_field(f)) == NULL ||
-       htsmsg_get_u32(c, "id", &id))
-      continue;
-
-    if((ch = channel_find_by_identifier(id)) == NULL)
-      continue;
-
-    if((s = htsmsg_get_str(c, "name")) != NULL)
-      channel_rename(ch, s);
-
-    if((s = htsmsg_get_str(c, "ch_icon")) != NULL)
-      channel_set_icon(ch, s);
-
-    if((s = htsmsg_get_str(c, "tags")) != NULL)
-      channel_set_tags_from_list(ch, s);
-
-    if((s = htsmsg_get_str(c, "epg_pre_start")) != NULL)
-      channel_set_epg_postpre_time(ch, 1, atoi(s));
-
-    if((s = htsmsg_get_str(c, "epg_post_end")) != NULL)
-      channel_set_epg_postpre_time(ch, 0, atoi(s));
-
-    if((s = htsmsg_get_str(c, "number")) != NULL)
-      channel_set_number(ch, atoi(s));
-
-    if((s = htsmsg_get_str(c, "epggrabsrc")) != NULL) {
-      char *tmp = strdup(s);
-      char *sptr = NULL, *sptr2 = NULL;
-      char *modecid  = strtok_r(tmp, ",", &sptr);
-      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) {
-            LIST_FOREACH(ecl, &ec->channels, link) {
-              if (ecl->channel == ch) {
-                LIST_REMOVE(ecl, link);
-                free(ecl);
-                mod->ch_save(mod, ec);
-                break;
-              }
-            }
-          }
-        }
-      }
-
-      /* Add new */
-      while (modecid) {
-        modid    = strtok_r(modecid, "|", &sptr2);
-        ecid     = strtok_r(NULL, "|", &sptr2);
-        modecid  = strtok_r(NULL, ",", &sptr);
-
-        if (!(mod = epggrab_module_find_by_id(modid)))
-          continue;
-        if (!mod->channels)
-          continue;
-        if (!(ec = epggrab_channel_find(mod->channels, ecid, 0, NULL, mod)))
-          continue;
-
-        epggrab_channel_link(ec, ch);
-      }
-
-      /* Cleanup */
-      free(tmp);
-    }
-  }
-}
-
-/**
- *
- */
-static htsmsg_t *
-build_record_channel ( channel_t *ch )
-{
-  char buf[1024];
-  channel_tag_mapping_t *ctm;
-  htsmsg_t *c;
-  char *epggrabsrc;
-  epggrab_module_t *mod;
-  epggrab_channel_t *ec;
-  epggrab_channel_link_t *ecl;
-
-  c = htsmsg_create_map();
-  htsmsg_add_str(c, "name", ch->ch_name);
-  htsmsg_add_u32(c, "chid", ch->ch_id);
-
-  if(ch->ch_icon != NULL) {
-    htsmsg_add_imageurl(c, "chicon", "imagecache/%d", ch->ch_icon);
-    htsmsg_add_str(c, "ch_icon", ch->ch_icon);
-  }
-
-  buf[0] = 0;
-  LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
-         snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
-                 "%s%d", strlen(buf) == 0 ? "" : ",",
-                 ctm->ctm_tag->ct_identifier);
-  }
-  htsmsg_add_str(c, "tags", buf);
-
-  htsmsg_add_s32(c, "epg_pre_start", ch->ch_dvr_extra_time_pre);
-  htsmsg_add_s32(c, "epg_post_end",  ch->ch_dvr_extra_time_post);
-  htsmsg_add_s32(c, "number",        ch->ch_number);
-
-  epggrabsrc = NULL;
-  LIST_FOREACH(mod, &epggrab_modules, link) {
-    if (mod->type != EPGGRAB_OTA && mod->channels) {
-      RB_FOREACH(ec, mod->channels, link) {
-        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);
-            }
-          }
-        }
-      }
-    }
-  }
-  if (epggrabsrc) htsmsg_add_str(c, "epggrabsrc", epggrabsrc);
-  free(epggrabsrc);
-  return c;
-}
-
-/**
- *
- */
-static int
-extjs_channels(http_connection_t *hc, const char *remain, void *opaque)
-{
-  htsbuf_queue_t *hq = &hc->hc_reply;
-  htsmsg_t *array;
-  channel_t *ch;
-  const char *op        = http_arg_get(&hc->hc_req_args, "op");
-  const char *entries   = http_arg_get(&hc->hc_req_args, "entries");
-
-  if(op == NULL)
-    return 400;
-
-  htsmsg_t *in =
-    entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
-
-  htsmsg_t *out = htsmsg_create_map();
-
-  scopedgloballock();
-
-  if(!strcmp(op, "list")) {
-    array = htsmsg_create_list();
-
-    RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
-      htsmsg_add_msg(array, NULL, build_record_channel(ch));
-    }
-    
-    htsmsg_add_msg(out, "entries", array);
-
-  } else if(!strcmp(op, "create")) {
-    htsmsg_destroy(out);
-    out = build_record_channel(channel_create(NULL));
-
-  } else if(!strcmp(op, "delete") && in != NULL) {
-    extjs_channels_delete(in);
-
-  } else if(!strcmp(op, "update") && in != NULL) {
-    extjs_channels_update(in);
-     
-  } else {
-    htsmsg_destroy(in);
-    htsmsg_destroy(out);
-    return 400;
-  }
-
-  htsmsg_json_serialize(out, hq, 0);
-  http_output_content(hc, "text/x-json; charset=UTF-8");
-  htsmsg_destroy(in);
-  htsmsg_destroy(out);
-  return 0;
-}
-
 /**
  * EPG Content Groups
  */
@@ -924,7 +708,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
     m = htsmsg_create_map();
 
     htsmsg_add_str(m, "channel", ch->ch_name);
-    htsmsg_add_u32(m, "channelid", ch->ch_id);
+    htsmsg_add_u32(m, "channelid", channel_get_id(ch));
     if(ch->ch_icon != NULL)
       htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
 
@@ -1186,7 +970,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
     const char *channel  = http_arg_get(&hc->hc_req_args, "channelid");
     const char *pri      = http_arg_get(&hc->hc_req_args, "pri");
 
-    channel_t *ch = channel ? channel_find_by_identifier(atoi(channel)) : NULL;
+    channel_t *ch = channel ? channel_find_by_id(atoi(channel)) : NULL;
 
     if(ch == NULL || title == NULL || 
        datestr  == NULL || strlen(datestr)  != 10 ||
@@ -1653,49 +1437,6 @@ extjs_servicedetails(http_connection_t *hc,
   return 0;
 }
 
-/**
- *
- */
-static int
-extjs_mergechannel(http_connection_t *hc, const char *remain, void *opaque)
-{
-  htsbuf_queue_t *hq = &hc->hc_reply;
-  const char *target = http_arg_get(&hc->hc_req_args, "targetID");
-  htsmsg_t *out;
-  channel_t *src, *dst;
-
-  if(remain == NULL || target == NULL)
-    return 400;
-
-  pthread_mutex_lock(&global_lock);
-
-  src = channel_find_by_identifier(atoi(remain));
-  dst = channel_find_by_identifier(atoi(target));
-
-  if(src == NULL || dst == NULL) {
-    pthread_mutex_unlock(&global_lock);
-    return 404;
-  }
-
-  out = htsmsg_create_map();
-
-  if(src != dst) {
-    channel_merge(dst, src);
-    htsmsg_add_u32(out, "success", 1);
-  } else {
-
-    htsmsg_add_u32(out, "success", 0);
-    htsmsg_add_str(out, "msg", "Target same as source");
-  }
-
-  pthread_mutex_unlock(&global_lock);
-
-  htsmsg_json_serialize(out, hq, 0);
-  htsmsg_destroy(out);
-  http_output_content(hc, "text/x-json; charset=UTF-8");
-  return 0;
-}
-
 /**
  *
  */
@@ -1972,67 +1713,6 @@ extjs_timeshift(http_connection_t *hc, const char *remain, void *opaque)
 }
 #endif
 
-static int
-extjs_service_mapping
-  (http_connection_t *hc, const char *remain, void *opaque)
-{
-  htsbuf_queue_t *hq = &hc->hc_reply;
-  htsmsg_t *out;
-  service_mapper_conf_t conf = { 0 };
-  const char *op = http_arg_get(&hc->hc_req_args, "op");
-
-  if (!op)
-    return HTTP_STATUS_BAD_REQUEST;
-
-  /* Status */
-  if (!strcmp(op, "status")) {
-    int num;
-    pthread_mutex_lock(&global_lock);
-    num = service_mapper_qlen();
-    pthread_mutex_unlock(&global_lock);
-    out = htsmsg_create_map();
-    htsmsg_add_u32(out, "remaining", num);
-
-  /* Start */
-  } else if (!strcmp(op, "start")) {
-    
-    /* Get config */
-    if (http_arg_get(&hc->hc_req_args, "check_availability") != NULL)
-      conf.check_availability = 1;
-    if (http_arg_get(&hc->hc_req_args, "encrypted") != NULL)
-      conf.encrypted          = 1;
-    if (http_arg_get(&hc->hc_req_args, "merge_same_name") != NULL)
-      conf.merge_same_name    = 1;
-    if (http_arg_get(&hc->hc_req_args, "provider_tags") != NULL)
-      conf.provider_tags      = 1;
-
-    /* Start */
-    pthread_mutex_lock(&global_lock);
-    service_mapper_start(&conf);
-    pthread_mutex_unlock(&global_lock);
-    out = htsmsg_create_map();
-  
-  /* Stop */
-  } else if (!strcmp(op, "stop")) {
-    pthread_mutex_lock(&global_lock);
-    service_mapper_stop();
-    pthread_mutex_unlock(&global_lock);
-    out = htsmsg_create_map();
-  
-  /* Invalid */
-  } else {
-    return HTTP_STATUS_BAD_REQUEST;
-  }
-
-
-  htsmsg_json_serialize(out, hq, 0);
-  htsmsg_destroy(out);
-  http_output_content(hc, "text/x-json; charset=UTF-8");
-
-  return 0;
-}
-
-
 /**
  * WEB user interface
  */
@@ -2043,7 +1723,6 @@ extjs_start(void)
   http_path_add("/extjs.html",       NULL, extjs_root,             ACCESS_WEB_INTERFACE);
   http_path_add("/capabilities",     NULL, extjs_capabilities,     ACCESS_WEB_INTERFACE);
   http_path_add("/tablemgr",         NULL, extjs_tablemgr,         ACCESS_WEB_INTERFACE);
-  http_path_add("/channels",         NULL, extjs_channels,         ACCESS_WEB_INTERFACE);
   http_path_add("/epggrab",          NULL, extjs_epggrab,          ACCESS_WEB_INTERFACE);
   http_path_add("/channeltags",      NULL, extjs_channeltags,      ACCESS_WEB_INTERFACE);
   http_path_add("/confignames",      NULL, extjs_confignames,      ACCESS_WEB_INTERFACE);
@@ -2059,15 +1738,12 @@ extjs_start(void)
   http_path_add("/ecglist",          NULL, extjs_ecglist,          ACCESS_WEB_INTERFACE);
   http_path_add("/config",           NULL, extjs_config,           ACCESS_WEB_INTERFACE);
   http_path_add("/languages",        NULL, extjs_languages,        ACCESS_WEB_INTERFACE);
-  http_path_add("/mergechannel",     NULL, extjs_mergechannel,     ACCESS_ADMIN);
   http_path_add("/servicedetails",   NULL, extjs_servicedetails,   ACCESS_ADMIN);
 #if ENABLE_TIMESHIFT
   http_path_add("/timeshift",        NULL, extjs_timeshift,        ACCESS_ADMIN);
 #endif
   http_path_add("/tvhlog",           NULL, extjs_tvhlog,           ACCESS_ADMIN);
 
-  http_path_add("/api/service_mapping", NULL, extjs_service_mapping, ACCESS_ADMIN);
-
 #if ENABLE_V4L
   extjs_start_v4l();
 #endif
index 3a30cd8f24d649832214392c5f2cfe1b185f41a6..1256c444abb3ff9f3afa9fd6b7099666b2a0f702 100644 (file)
@@ -55,7 +55,7 @@ dumpchannels(htsbuf_queue_t *hq)
   channel_t *ch;
   outputtitle(hq, 0, "Channels");
 
-  RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
+  CHANNEL_FOREACH(ch) {
     
     htsbuf_qprintf(hq, "%s (%d)\n", ch->ch_name, ch->ch_id);
     htsbuf_qprintf(hq,
index bf1ba0aaffa9e4e7f2ae21ce6f1cd38dbb504194..d04be40b6bcf1c19ea9407a49f1eaca4bd1fc4ad 100644 (file)
@@ -25,6 +25,7 @@ tvheadend.channelrec = new Ext.data.Record.create(
        [ 'name', 'chid', 'epggrabsrc', 'tags', 'ch_icon', 'epg_pre_start',
                'epg_post_end', 'number' ]);
 
+/*
 tvheadend.channels = new Ext.data.JsonStore({
        autoLoad : true,
        root : 'entries',
@@ -43,6 +44,7 @@ tvheadend.channels = new Ext.data.JsonStore({
 tvheadend.comet.on('channels', function(m) {
        if (m.reload != null) tvheadend.channels.reload();
 });
+*/
 
 /*
  * Service mapping
@@ -466,3 +468,24 @@ tvheadend.chconf = function() {
 
        return grid;
 }
+
+tvheadend.channel_tab = function(panel)
+{
+  var mapButton = new Ext.Toolbar.Button({
+    tooltip : 'Map services to channels',
+    iconCls : '',
+    text    : 'Map Services',
+    handler : tvheadend.mapServices,
+    disabled : false,
+  });
+
+  tvheadend.idnode_grid(panel, {
+    url     : 'api/channel',
+    comet   : 'channel',
+    titleS  : 'Channel',
+    titleP  : 'Channels',
+    add     : false,
+    del     : false,
+    tbar    : [ mapButton ]
+  });
+}
index b339f11cb7566216b7d45e5343afda1dd66cd98a..ed59c49144cc8916c20d4cac4d5215e372a71a0a 100644 (file)
@@ -111,7 +111,10 @@ tvheadend.idnode_editor_field = function(f, create)
   switch(f.type) {
     case 'str':
       if (f.enum) {
-        return new Ext.form.ComboBox({
+        var cons = Ext.form.ComboBox;
+        if (f.multi)
+          cons = Ext.ux.form.LovCombo;
+        return new cons({
           fieldLabel      : f.caption,
           name            : f.id,
           value           : f.value,
@@ -124,7 +127,17 @@ tvheadend.idnode_editor_field = function(f, create)
           typeAhead       : true,
           forceSelection  : true,
           triggerAction   : 'all',
-          emptyText       :'Select ' + f.caption +' ...'
+          emptyText       :'Select ' + f.caption +' ...',
+          listeners       : { 
+            keyup: function() {
+              this.store.filter('val', this.getRawValue(), true, false);
+            },
+            beforequery: function(queryEvent) {
+              queryEvent.combo.onLoad();
+              // prevent doQuery from firing and clearing out my filter.
+              return false; 
+            }
+          }
         });
       } else {
         return new Ext.form.TextField({
@@ -615,14 +628,24 @@ tvheadend.idnode_grid = function(panel, conf)
       }
     });
     buttons.push(editBtn);
-    buttons.push('->');
+
+    /* Extra buttons */
+    if (conf.tbar) {
+      buttons.push('-')
+      for (i = 0; i < conf.tbar.length; i++)
+        buttons.push(conf.tbar[i])
+    }
+
+    /* Help */
     if (conf.help) {
+      buttons.push('->');
       buttons.push({  
         text    : 'Help',
         handler : conf.help
       });
     }
 
+
     /* Grid Panel */
     var auto   = new Ext.form.Checkbox({
       checked     : true,
index a3280f2af55f0b579ddb215aa61c9887164a6d92..49ecb8f89a239f2108baaf3b43f666b58adf6e5e 100644 (file)
@@ -290,11 +290,11 @@ function accessUpdate(o) {
       title : 'Channel / EPG',
       iconCls : 'television',
       items : [
-        new tvheadend.chconf,
         new tvheadend.cteditor,
         new tvheadend.epggrab
       ]
     });
+    tvheadend.channel_tab(tvheadend.conf_chepg);
     tabs1.push(tvheadend.conf_chepg);
 
     /* DVR / Timeshift */
index 36ac4d071ad47d7267028639ea342c0b99a57e20..1e17c8d255231a8401047fbfab9dc9440f5a6db7 100644 (file)
@@ -290,7 +290,7 @@ http_channel_playlist(http_connection_t *hc, channel_t *channel)
   hq = &hc->hc_reply;
   host = http_arg_get(&hc->hc_args, "Host");
 
-  snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel->ch_id);
+  snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(channel));
 
   htsbuf_qprintf(hq, "#EXTM3U\n");
   htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel->ch_name);
@@ -319,7 +319,7 @@ http_tag_playlist(http_connection_t *hc, channel_tag_t *tag)
 
   htsbuf_qprintf(hq, "#EXTM3U\n");
   LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
-    snprintf(buf, sizeof(buf), "/stream/channelid/%d", ctm->ctm_channel->ch_id);
+    snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(ctm->ctm_channel));
     htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ctm->ctm_channel->ch_name);
     htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, 
        access_ticket_create(buf));
@@ -377,8 +377,8 @@ http_channel_list_playlist(http_connection_t *hc)
   host = http_arg_get(&hc->hc_args, "Host");
 
   htsbuf_qprintf(hq, "#EXTM3U\n");
-  RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
-    snprintf(buf, sizeof(buf), "/stream/channelid/%d", ch->ch_id);
+  CHANNEL_FOREACH(ch) {
+    snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(ch));
 
     htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ch->ch_name);
     htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf, 
@@ -511,9 +511,9 @@ page_http_playlist(http_connection_t *hc, const char *remain, void *opaque)
   pthread_mutex_lock(&global_lock);
 
   if(nc == 2 && !strcmp(components[0], "channelid"))
-    ch = channel_find_by_identifier(atoi(components[1]));
+    ch = channel_find_by_id(atoi(components[1]));
   else if(nc == 2 && !strcmp(components[0], "channel"))
-    ch = channel_find_by_name(components[1], 0, 0);
+    ch = channel_find_by_name(components[1]);
   else if(nc == 2 && !strcmp(components[0], "dvrid"))
     de = dvr_entry_find_by_id(atoi(components[1]));
   else if(nc == 2 && !strcmp(components[0], "tagid"))
@@ -748,9 +748,9 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
   scopedgloballock();
 
   if(!strcmp(components[0], "channelid")) {
-    ch = channel_find_by_identifier(atoi(components[1]));
+    ch = channel_find_by_id(atoi(components[1]));
   } else if(!strcmp(components[0], "channel")) {
-    ch = channel_find_by_name(components[1], 0, 0);
+    ch = channel_find_by_name(components[1]);
   } else if(!strcmp(components[0], "service")) {
     service = service_find_by_identifier(components[1]);
 #if 0//ENABLE_LINUXDVB