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)
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;
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);
+ }
+ }
+ }
}
channel_save(channel_t *ch)
{
htsmsg_t *m = htsmsg_create();
+ htsmsg_t *tags;
+ channel_tag_mapping_t *ctm;
lock_assert(&global_lock);
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);
}
{
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);
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;
+}
/**
snprintf(buf, sizeof(buf), "%d", tally);
id = buf;
} else {
- tally = atoi(id);
+ tally = MAX(atoi(id), tally);
}
ct->ct_identifier = strdup(id);
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);
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
*/
LIST_ENTRY(channel_tag_mapping) ctm_tag_link;
channel_tag_t *ctm_tag;
+ int ctm_mark;
+
} channel_tag_mapping_t;
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 */
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);
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")) {
} 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);
}
+
+/**
+ *
+ */
+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
*/
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);
}
+
+/**
+ * Channel tags
+ */
+
+tvheadend.channelTags = new Ext.data.JsonStore({
+ autoLoad:true,
+ root:'entries',
+ fields: [{name: 'identifier'}, {name: 'name'}],
+ url:'channeltags',
+ baseParams: {op: 'listTags'}
+});
+
+
/**
* Channel details
*/
var confreader = new Ext.data.JsonReader({
root: 'channels',
- }, ['name', 'comdetect','xmltvchannel']);
+ }, ['name','xmltvchannel','tags']);
var xmltvChannels = new Ext.data.JsonStore({
baseParams: {op: 'listChannels'}
});
+
var confpanel = new Ext.FormPanel({
border:false,
disabled:true,
labelWidth: 150,
waitMsgTarget: true,
reader: confreader,
- // defaultType: 'textfield',
items: [{
layout:'column',
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,
+ }]
+ }]
}]
});
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');