]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Add support for mapping tags to channels.
authorAndreas Öman <andreas@lonelycoder.com>
Sun, 7 Sep 2008 08:54:16 +0000 (08:54 +0000)
committerAndreas Öman <andreas@lonelycoder.com>
Sun, 7 Sep 2008 08:54:16 +0000 (08:54 +0000)
channels.c
channels.h
webui/extjs.c
webui/static/app/chconf.js
webui/static/app/tvheadend.js

index d129216196d9678e6a16197cfed934a7cf7c9dec..a3b38a5940d4579cd1b36ba0c4142996685aa7cc 100644 (file)
@@ -45,7 +45,11 @@ struct channel_list channels_not_xmltv_mapped;
 
 struct channel_tree channel_name_tree;
 static struct channel_tree channel_identifier_tree;
-static struct channel_tag_queue channel_tags;
+struct channel_tag_queue channel_tags;
+
+static void channel_tag_map(channel_t *ch, channel_tag_t *ct, int check);
+static channel_tag_t *channel_tag_find(const char *id, int create);
+static void channel_tag_mapping_destroy(channel_tag_mapping_t *ctm);
 
 static int
 dictcmp(const char *a, const char *b)
@@ -212,6 +216,9 @@ channel_load_one(htsmsg_t *c, int id)
   channel_t *ch;
   const char *s;
   const char *name = htsmsg_get_str(c, "name");
+  htsmsg_t *tags;
+  htsmsg_field_t *f;
+  channel_tag_t *ct;
 
   if(name == NULL)
     return;
@@ -238,6 +245,15 @@ channel_load_one(htsmsg_t *c, int id)
     LIST_INSERT_HEAD(&ch->ch_xc->xc_channels, ch, ch_xc_link);
   else
     LIST_INSERT_HEAD(&channels_not_xmltv_mapped, ch, ch_xc_link);
+
+  if((tags = htsmsg_get_array(c, "tags")) != NULL) {
+    HTSMSG_FOREACH(f, tags) {
+      if(f->hmf_type == HMF_STR && 
+        (ct = channel_tag_find(f->hmf_str, 0)) != NULL) {
+       channel_tag_map(ch, ct, 1);
+      }
+    }
+  }
 }
 
 
@@ -267,6 +283,8 @@ static void
 channel_save(channel_t *ch)
 {
   htsmsg_t *m = htsmsg_create();
+  htsmsg_t *tags;
+  channel_tag_mapping_t *ctm;
 
   lock_assert(&global_lock);
 
@@ -282,6 +300,12 @@ channel_save(channel_t *ch)
                 val2str(ch->ch_commercial_detection,
                         commercial_detect_tab) ?: "?");
   
+  tags = htsmsg_create_array();
+  LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link)
+    htsmsg_add_str(tags, NULL, ctm->ctm_tag->ct_identifier);
+
+  htsmsg_add_msg(m, "tags", tags);
+
   hts_settings_save(m, "channels/%d", ch->ch_id);
   htsmsg_destroy(m);
 }
@@ -323,9 +347,13 @@ channel_delete(channel_t *ch)
 {
   th_transport_t *t;
   th_subscription_t *s;
+  channel_tag_mapping_t *ctm;
 
   lock_assert(&global_lock);
 
+  while((ctm = LIST_FIRST(&ch->ch_ctms)) != NULL)
+    channel_tag_mapping_destroy(ctm);
+
   tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" deleted",
         ch->ch_name);
 
@@ -428,6 +456,100 @@ channel_set_xmltv_source(channel_t *ch, xmltv_channel_t *xc)
   channel_save(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);
+    }
+  }
+
+  if(change)
+    channel_save(ch);
+}
+
+
+
+
+/**
+ *
+ */
+static void
+channel_tag_map(channel_t *ch, channel_tag_t *ct, int check)
+{
+  channel_tag_mapping_t *ctm;
+
+  if(check) {
+    LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link)
+      if(ctm->ctm_tag == ct)
+       return;
+
+    LIST_FOREACH(ctm, &ct->ct_ctms, ctm_tag_link)
+      if(ctm->ctm_channel == ch)
+       return;
+  }
+
+  LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link)
+    assert(ctm->ctm_tag != ct);
+
+  LIST_FOREACH(ctm, &ct->ct_ctms, ctm_tag_link)
+    assert(ctm->ctm_channel != ch);
+
+  ctm = malloc(sizeof(channel_tag_mapping_t));
+
+  ctm->ctm_channel = ch;
+  LIST_INSERT_HEAD(&ch->ch_ctms, ctm, ctm_channel_link);
+
+  ctm->ctm_tag = ct;
+  LIST_INSERT_HEAD(&ct->ct_ctms, ctm, ctm_tag_link);
+
+  ctm->ctm_mark = 0;
+}
 
 
 /**
@@ -467,7 +589,7 @@ channel_tag_find(const char *id, int create)
     snprintf(buf, sizeof(buf), "%d", tally);
     id = buf;
   } else {
-    tally = atoi(id);
+    tally = MAX(atoi(id), tally);
   }
 
   ct->ct_identifier = strdup(id);
@@ -485,9 +607,13 @@ static void
 channel_tag_destroy(channel_tag_t *ct)
 {
   channel_tag_mapping_t *ctm;
+  channel_t *ch;
 
-  while((ctm = LIST_FIRST(&ct->ct_ctms)) != NULL)
+  while((ctm = LIST_FIRST(&ct->ct_ctms)) != NULL) {
+    ch = ctm->ctm_channel;
     channel_tag_mapping_destroy(ctm);
+    channel_save(ch);
+  }
 
   free(ct->ct_identifier);
   free(ct->ct_name);
index 49ad4750d513a40c87854089b5db7d484427345a..27d5944fb2d9891e724637cab434e7bb02d9db95 100644 (file)
@@ -22,6 +22,9 @@
 LIST_HEAD(channel_tag_mapping_list, channel_tag_mapping);
 TAILQ_HEAD(channel_tag_queue, channel_tag);
 
+extern struct channel_tag_queue channel_tags;
+
+
 /*
  * Channel definition
  */ 
@@ -88,6 +91,8 @@ typedef struct channel_tag_mapping {
   LIST_ENTRY(channel_tag_mapping) ctm_tag_link;
   channel_tag_t *ctm_tag;
 
+  int ctm_mark;
+
 } channel_tag_mapping_t;
 
 
@@ -113,6 +118,8 @@ void channel_set_icon(channel_t *ch, const char *icon);
 struct xmltv_channel;
 void channel_set_xmltv_source(channel_t *ch, struct xmltv_channel *xc);
 
+void channel_set_tags_from_list(channel_t *ch, const char *maplist);
+
 extern struct channel_list channels_not_xmltv_mapped;
 
 #endif /* CHANNELS_H */
index 643a30d630a634afe99a14333c218eb3dc77dfa7..a29eb183fa03e06334cf64bf0436330a13b31847 100644 (file)
@@ -566,6 +566,8 @@ extjs_channel(http_connection_t *hc, const char *remain, void *opaque)
   th_transport_t *t;
   int reloadchlist = 0;
   htsmsg_t *out, *array, *r;
+  channel_tag_mapping_t *ctm;
+  char buf[200];
 
   pthread_mutex_lock(&global_lock);
 
@@ -584,6 +586,14 @@ extjs_channel(http_connection_t *hc, const char *remain, void *opaque)
     if(ch->ch_xc != NULL)
       htsmsg_add_str(r, "xmltvchannel", ch->ch_xc->xc_displayname);
 
+    buf[0] = 0;
+    LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
+      snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+              "%s%s", strlen(buf) == 0 ? "" : ",",
+              ctm->ctm_tag->ct_identifier);
+    }
+    htsmsg_add_str(r, "tags", buf);
+
     out = json_single_record(r, "channels");
 
   } else if(!strcmp(op, "gettransports")) {
@@ -620,6 +630,9 @@ extjs_channel(http_connection_t *hc, const char *remain, void *opaque)
 
   } else if(!strcmp(op, "save")) {
 
+    if((s = http_arg_get(&hc->hc_req_args, "tags")) != NULL)
+      channel_set_tags_from_list(ch, s);
+
     s = http_arg_get(&hc->hc_req_args, "xmltvchannel");
     channel_set_xmltv_source(ch, s?xmltv_channel_find_by_displayname(s):NULL);
 
@@ -699,6 +712,52 @@ extjs_xmltv(http_connection_t *hc, const char *remain, void *opaque)
 
 }
 
+
+/**
+ *
+ */
+static int
+extjs_channeltags(http_connection_t *hc, const char *remain, void *opaque)
+{
+  htsbuf_queue_t *hq = &hc->hc_reply;
+  const char *op = http_arg_get(&hc->hc_req_args, "op");
+  htsmsg_t *out, *array, *e;
+  channel_tag_t *ct;
+
+  pthread_mutex_lock(&global_lock);
+
+  if(!strcmp(op, "listTags")) {
+
+    out = htsmsg_create();
+    array = htsmsg_create_array();
+
+    TAILQ_FOREACH(ct, &channel_tags, ct_link) {
+      if(!ct->ct_enabled)
+       continue;
+
+      e = htsmsg_create();
+      htsmsg_add_str(e, "identifier", ct->ct_identifier);
+      htsmsg_add_str(e, "name", ct->ct_name);
+      htsmsg_add_msg(array, NULL, e);
+    }
+
+    htsmsg_add_msg(out, "entries", array);
+
+  } else {
+    pthread_mutex_unlock(&global_lock);
+    return HTTP_STATUS_BAD_REQUEST;
+  }
+
+  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;
+
+}
+
+
 /**
  * WEB user interface
  */
@@ -713,4 +772,5 @@ extjs_start(void)
   http_path_add("/chlist",      NULL, extjs_chlist,      ACCESS_WEB_INTERFACE);
   http_path_add("/channel",     NULL, extjs_channel,     ACCESS_WEB_INTERFACE);
   http_path_add("/xmltv",       NULL, extjs_xmltv,       ACCESS_WEB_INTERFACE);
+  http_path_add("/channeltags", NULL, extjs_channeltags, ACCESS_WEB_INTERFACE);
 }
index d3d91eebd995f6484e061261f46998a211b856e2..e72c226991f62846149bd9be6952755aa139b789 100644 (file)
@@ -1,3 +1,17 @@
+
+/**
+ * Channel tags
+ */
+
+tvheadend.channelTags = new Ext.data.JsonStore({
+    autoLoad:true,
+    root:'entries',
+    fields: [{name: 'identifier'}, {name: 'name'}],
+    url:'channeltags',
+    baseParams: {op: 'listTags'}
+});
+
+
 /**
  * Channel details
  */
@@ -89,7 +103,7 @@ tvheadend.channeldetails = function(chid, chname) {
 
     var confreader = new Ext.data.JsonReader({
        root: 'channels',
-    }, ['name', 'comdetect','xmltvchannel']);
+    }, ['name','xmltvchannel','tags']);
 
     
     var xmltvChannels = new Ext.data.JsonStore({
@@ -99,6 +113,7 @@ tvheadend.channeldetails = function(chid, chname) {
        baseParams: {op: 'listChannels'}
     });
 
     var confpanel = new Ext.FormPanel({
        border:false,
        disabled:true,
@@ -108,7 +123,6 @@ tvheadend.channeldetails = function(chid, chname) {
        labelWidth: 150,
        waitMsgTarget: true,
        reader: confreader,
-       //          defaultType: 'textfield',
 
        items: [{
            layout:'column',
@@ -118,68 +132,40 @@ tvheadend.channeldetails = function(chid, chname) {
                columnWidth:.5,
                layout: 'form',
                defaultType: 'textfield',
+               items: [
+                   {
+                       fieldLabel: 'Channel name',
+                       name: 'name',
+                   },new Ext.form.ComboBox({
+                       loadingText: 'Loading...',
+                       fieldLabel: 'XML-TV Source',
+                       name: 'xmltvchannel',
+                       width: 300,
+                       displayField:'xcTitle',
+                       valueField:'xcTitle',
+                       store: xmltvChannels,
+                       forceSelection: true,
+                       mode: 'remote',
+                       editable: false,
+                       triggerAction: 'all',
+                       emptyText: 'None'
+                   })
+               ]
+           },{
+               border:false,
+               columnWidth:.5,
+               layout: 'form',
                items: [{
-                   
-                   fieldLabel: 'Channel name',
-                   name: 'name',
-               },
-                       new Ext.form.ComboBox({
-                           loadingText: 'Loading...',
-                           fieldLabel: 'XML-TV Source',
-                           name: 'xmltvchannel',
-                           width: 300,
-                           displayField:'xcTitle',
-                           valueField:'xcTitle',
-                           store: xmltvChannels,
-                           forceSelection: true,
-                           mode: 'remote',
-                           editable: false,
-                           triggerAction: 'all',
-                           emptyText: 'None'
-                       })
-                       /*
-                               ,
-                               new Ext.form.ComboBox({
-                                       allowBlank: false,
-                                       fieldLabel: 'Commercial detection',
-                                       name: 'comdetect',
-                                       displayField:'mode',
-                                       valueField:'imode',
-                                       mode: 'local',
-                                       triggerAction: 'all',
-                                       selectOnFocus:true,
-                                       editable:false,
-                                       store: new Ext.data.SimpleStore({
-                                               fields: ['imode', 'mode'],
-                                               data: [
-                                                      ['none', 'None'],
-                                                      ['tt192', 'Teletext page 192']]
-                                           })
-                                   })
-                               */
-                      ]
-           }
-                  /*
-                       ,{
-                           columnWidth:.5,
-                           layout: 'form',
-                           items: [{
-                                   xtype: 'checkboxgroup',
-                                   fieldLabel: 'Tags',
-                                   itemCls: 'x-check-group-alt',
-                                   columns: 1,
-                                   vertical: true,
-                                   items: [{
-                                           boxLabel: 'Favourites', name: 'favourite'},{
-                                           boxLabel: 'Sports', name: 'sports'},{
-                                           boxLabel: 'News', name: 'news'},{
-                                           boxLabel: 'Movies', name: 'movies'},{
-                                           boxLabel: 'Children', name: 'children'}
-                                       ]
-                               }
-                               ]
-                               } */
-                 ]
+                   fieldLabel: 'Tags',
+                   xtype:"multiselect",
+                   name:"tags",
+                   valueField:"identifier",
+                   displayField:"name",
+                   width:250,
+                   height:200,
+                   store:tvheadend.channelTags,
+               }]
+           }]
        }]
     });
 
index f5aaa734dd413a00e9ae37b3375a07c767fc13c8..581efdc8825a8734aad3ac80896cbb978da8e689 100644 (file)
@@ -14,6 +14,12 @@ tvheadend.comet_poller = function() {
            var m = response.messages[x];
            
            switch(m.notificationClass) {
+           case 'channeltags':
+               if(m.reload != null) {
+                   tvheadend.channelTags.reload();
+               }
+               break;
+
            case 'logmessage':
                
                var sl = Ext.get('systemlog');