#include "pvr.h"
#include "autorec.h"
#include "xmltv.h"
+#include "dtable.h"
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;
static int
dictcmp(const char *a, const char *b)
}
-/**
- *
- */
-void
-channels_init(void)
-{
- channels_load();
-}
-
-
-
/**
* Write out a config file for a channel
*/
ch->ch_xc = xc;
channel_save(ch);
}
+
+
+
+/**
+ *
+ */
+static void
+channel_tag_mapping_destroy(channel_tag_mapping_t *ctm)
+{
+ LIST_REMOVE(ctm, ctm_channel_link);
+ LIST_REMOVE(ctm, ctm_tag_link);
+ free(ctm);
+}
+
+
+/**
+ *
+ */
+static channel_tag_t *
+channel_tag_find(const char *id, int create)
+{
+ channel_tag_t *ct;
+ char buf[20];
+ static int tally;
+
+ if(id != NULL) {
+ TAILQ_FOREACH(ct, &channel_tags, ct_link)
+ if(!strcmp(ct->ct_identifier, id))
+ return ct;
+ }
+
+ if(create == 0)
+ return NULL;
+
+ ct = calloc(1, sizeof(channel_tag_t));
+ if(id == NULL) {
+ tally++;
+ snprintf(buf, sizeof(buf), "%d", tally);
+ id = buf;
+ } else {
+ tally = atoi(id);
+ }
+
+ ct->ct_identifier = strdup(id);
+ ct->ct_name = strdup("New tag");
+ ct->ct_comment = strdup("");
+ TAILQ_INSERT_TAIL(&channel_tags, ct, ct_link);
+ return ct;
+}
+
+
+/**
+ *
+ */
+static void
+channel_tag_destroy(channel_tag_t *ct)
+{
+ channel_tag_mapping_t *ctm;
+
+ while((ctm = LIST_FIRST(&ct->ct_ctms)) != NULL)
+ channel_tag_mapping_destroy(ctm);
+
+ free(ct->ct_identifier);
+ free(ct->ct_name);
+ free(ct->ct_comment);
+ TAILQ_REMOVE(&channel_tags, ct, ct_link);
+ free(ct);
+}
+
+
+/**
+ *
+ */
+static htsmsg_t *
+channel_tag_record_build(channel_tag_t *ct)
+{
+ htsmsg_t *e = htsmsg_create();
+ htsmsg_add_u32(e, "enabled", !!ct->ct_enabled);
+ htsmsg_add_u32(e, "internal", !!ct->ct_internal);
+
+ htsmsg_add_str(e, "name", ct->ct_name);
+ htsmsg_add_str(e, "comment", ct->ct_comment);
+ htsmsg_add_str(e, "id", ct->ct_identifier);
+ return e;
+}
+
+
+/**
+ *
+ */
+static htsmsg_t *
+channel_tag_record_get_all(void *opaque)
+{
+ htsmsg_t *r = htsmsg_create_array();
+ channel_tag_t *ct;
+
+ TAILQ_FOREACH(ct, &channel_tags, ct_link)
+ htsmsg_add_msg(r, NULL, channel_tag_record_build(ct));
+
+ return r;
+}
+
+
+/**
+ *
+ */
+static htsmsg_t *
+channel_tag_record_get(void *opaque, const char *id)
+{
+ channel_tag_t *ct;
+
+ if((ct = channel_tag_find(id, 0)) == NULL)
+ return NULL;
+ return channel_tag_record_build(ct);
+}
+
+
+/**
+ *
+ */
+static htsmsg_t *
+channel_tag_record_create(void *opaque)
+{
+ return channel_tag_record_build(channel_tag_find(NULL, 1));
+}
+
+
+/**
+ *
+ */
+static htsmsg_t *
+channel_tag_record_update(void *opaque, const char *id, htsmsg_t *values,
+ int maycreate)
+{
+ channel_tag_t *ct;
+ uint32_t u32;
+
+ if((ct = channel_tag_find(id, maycreate)) == NULL)
+ return NULL;
+
+ tvh_str_update(&ct->ct_name, htsmsg_get_str(values, "name"));
+ tvh_str_update(&ct->ct_comment, htsmsg_get_str(values, "comment"));
+
+ if(!htsmsg_get_u32(values, "enabled", &u32))
+ ct->ct_enabled = u32;
+
+ if(!htsmsg_get_u32(values, "internal", &u32))
+ ct->ct_internal = u32;
+
+ return channel_tag_record_build(ct);
+}
+
+
+/**
+ *
+ */
+static int
+channel_tag_record_delete(void *opaque, const char *id)
+{
+ channel_tag_t *ct;
+
+ if((ct = channel_tag_find(id, 0)) == NULL)
+ return -1;
+ channel_tag_destroy(ct);
+ return 0;
+}
+
+
+/**
+ *
+ */
+static const dtable_class_t channel_tags_dtc = {
+ .dtc_record_get = channel_tag_record_get,
+ .dtc_record_get_all = channel_tag_record_get_all,
+ .dtc_record_create = channel_tag_record_create,
+ .dtc_record_update = channel_tag_record_update,
+ .dtc_record_delete = channel_tag_record_delete,
+};
+
+
+/**
+ *
+ */
+void
+channels_init(void)
+{
+ dtable_t *dt;
+
+ TAILQ_INIT(&channel_tags);
+
+ dt = dtable_create(&channel_tags_dtc, "channeltags", NULL);
+ dtable_load(dt);
+
+ channels_load();
+}
#ifndef CHANNELS_H
#define CHANNELS_H
+LIST_HEAD(channel_tag_mapping_list, channel_tag_mapping);
+TAILQ_HEAD(channel_tag_queue, channel_tag);
/*
* Channel definition
struct xmltv_channel *ch_xc;
LIST_ENTRY(channel) ch_xc_link;
+ struct channel_tag_mapping_list ch_ctms;
+
} channel_t;
+
+/**
+ * Channel tag
+ */
+typedef struct channel_tag {
+ TAILQ_ENTRY(channel_tag) ct_link;
+ int ct_enabled;
+ int ct_internal;
+ char *ct_name;
+ char *ct_comment;
+ char *ct_identifier;
+ struct channel_tag_mapping_list ct_ctms;
+} channel_tag_t;
+
+
+/**
+ * Channel tag mapping
+ */
+typedef struct channel_tag_mapping {
+ LIST_ENTRY(channel_tag_mapping) ctm_channel_link;
+ channel_t *ctm_channel;
+
+ LIST_ENTRY(channel_tag_mapping) ctm_tag_link;
+ channel_tag_t *ctm_tag;
+
+} channel_tag_mapping_t;
+
+
+
void channels_init(void);
channel_t *channel_find_by_name(const char *name, int create);
/**
* Load all components
*/
+ extjs_load(hq, "static/app/cteditor.js");
extjs_load(hq, "static/app/acleditor.js");
extjs_load(hq, "static/app/cwceditor.js");
extjs_load(hq, "static/app/dvb.js");
--- /dev/null
+
+tvheadend.cteditor = function() {
+ var fm = Ext.form;
+
+ var enabledColumn = new Ext.grid.CheckColumn({
+ header: "Enabled",
+ dataIndex: 'enabled',
+ width: 60
+ });
+
+ var internalColumn = new Ext.grid.CheckColumn({
+ header: "Internal",
+ dataIndex: 'internal',
+ width: 100
+ });
+
+
+ var cm = new Ext.grid.ColumnModel([
+ enabledColumn,
+ {
+ id:'name',
+ header: "Name",
+ dataIndex: 'name',
+ editor: new fm.TextField({
+ allowBlank: false
+ })
+ },
+ internalColumn,
+ {
+ id:'comment',
+ header: "Comment",
+ dataIndex: 'comment',
+ width: 400,
+ editor: new fm.TextField({
+
+ })
+ }
+ ]);
+
+
+ cm.defaultSortable = true;
+
+ var ChannelTagRecord = Ext.data.Record.create([
+ {name: 'enabled'},
+ {name: 'name'},
+ {name: 'internal'},
+ {name: 'comment'}
+ ]);
+
+ var store = new Ext.data.JsonStore({
+ root: 'entries',
+ fields: ChannelTagRecord,
+ url: "tablemgr",
+ autoLoad: true,
+ id: 'id',
+ storeid: 'id',
+ baseParams: {table: "channeltags", op: "get"}
+ });
+
+
+ function addRecord() {
+ Ext.Ajax.request({
+ url: "tablemgr", params: {op:"create", table:"channeltags"},
+ failure:function(response,options){
+ Ext.MessageBox.alert('Server Error','Unable to generate new record');
+ },
+ success:function(response,options){
+ var responseData = Ext.util.JSON.decode(response.responseText);
+
+ var p = new ChannelTagRecord(responseData, responseData.id);
+ grid.stopEditing();
+ store.insert(0, p);
+ grid.startEditing(0, 0);
+ }
+ })
+ };
+
+ function delSelected() {
+ var selectedKeys = grid.selModel.selections.keys;
+ if(selectedKeys.length > 0)
+ {
+ Ext.MessageBox.confirm('Message',
+ 'Do you really want to delete selection?',
+ deleteRecord);
+ } else {
+ Ext.MessageBox.alert('Message',
+ 'Please select at least one item to delete');
+ }
+ };
+
+
+ function deleteRecord(btn) {
+ if(btn=='yes') {
+ var selectedKeys = grid.selModel.selections.keys;
+
+ Ext.Ajax.request({
+ url: "tablemgr", params: {op:"delete", table:"channeltags",
+ entries:Ext.encode(selectedKeys)},
+ failure:function(response,options){
+ Ext.MessageBox.alert('Server Error','Unable to delete');
+ },
+ success:function(response,options){
+ store.reload();
+ }
+ })
+ }
+ }
+
+
+
+ function saveChanges() {
+ var mr = store.getModifiedRecords();
+ var out = new Array();
+ for (var x = 0; x < mr.length; x++) {
+ v = mr[x].getChanges();
+ out[x] = v;
+ out[x].id = mr[x].id;
+ }
+
+ Ext.Ajax.request({url: "tablemgr",
+ params: {op:"update", table:"channeltags",
+ entries:Ext.encode(out)},
+ success:function(response,options){
+ store.commitChanges();
+ },
+ failure:function(response,options){
+ Ext.MessageBox.alert('Message',response.statusText);
+ }
+ });
+ }
+
+ var grid = new Ext.grid.EditorGridPanel({
+ title: 'Channel Tags',
+ plugins:[enabledColumn,internalColumn],
+ store: store,
+ clicksToEdit: 2,
+ cm: cm,
+ selModel: new Ext.grid.RowSelectionModel({singleSelect:false}),
+ tbar: [{
+ tooltip: 'Create a new channel tag entry on the server. ' +
+ 'The new entry is initially disabled so it must be enabled before it start taking effect.',
+ iconCls:'add',
+ text: 'Add entry',
+ handler: addRecord
+ }, '-', {
+ tooltip: 'Delete one or more selected rows',
+ iconCls:'remove',
+ text: 'Delete selected',
+ handler: delSelected
+ }, '-', {
+ tooltip: 'Save any changes made (Changed cells have red borders).',
+ iconCls:'save',
+ text: "Save changes",
+ handler: saveChanges
+ }
+ ]
+ });
+
+ return grid;
+}
autoScroll:true,
title: 'Configuration',
items: [new tvheadend.chconf,
+ new tvheadend.cteditor,
new tvheadend.dvb,
new tvheadend.acleditor,
new tvheadend.cwceditor]