Add support for mapping tags to channels.
This commit is contained in:
parent
d8fca6b5f0
commit
704f80a017
5 changed files with 251 additions and 66 deletions
132
channels.c
132
channels.c
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
Loading…
Add table
Reference in a new issue