From 3c088cb271d9cb3c27389eb2a18e604487aaadfe Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 21 Aug 2014 13:20:28 +0200 Subject: [PATCH] channel: tags - move to idnode system --- src/api/api_channel.c | 58 +++++-- src/channels.c | 276 ++++++++++++------------------ src/channels.h | 15 +- src/epg.c | 2 +- src/htsp_server.c | 6 +- src/webui/extjs.c | 45 ----- src/webui/static/app/chconf.js | 23 ++- src/webui/static/app/cteditor.js | 69 +++----- src/webui/static/app/dvr.js | 2 +- src/webui/static/app/epg.js | 8 +- src/webui/static/app/idnode.js | 1 + src/webui/static/app/tvheadend.js | 9 +- src/webui/webui.c | 2 +- 13 files changed, 213 insertions(+), 303 deletions(-) diff --git a/src/api/api_channel.c b/src/api/api_channel.c index 6bf7ec98..99f73c9f 100644 --- a/src/api/api_channel.c +++ b/src/api/api_channel.c @@ -78,27 +78,50 @@ api_channel_create } static int -api_channeltag_enum +api_channel_tag_list ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) { channel_tag_t *ct; htsmsg_t *l, *e; - int _enum = htsmsg_get_bool_or_default(args, "enum", 0); - - if (_enum) { - l = htsmsg_create_list(); - TAILQ_FOREACH(ct, &channel_tags, ct_link) { - e = htsmsg_create_map(); - htsmsg_add_u32(e, "key", ct->ct_identifier); - htsmsg_add_str(e, "val", ct->ct_name); - htsmsg_add_msg(l, NULL, e); - } - *resp = htsmsg_create_map(); - htsmsg_add_msg(*resp, "entries", l); - } else { - // TODO: support full listing v enum + l = htsmsg_create_list(); + TAILQ_FOREACH(ct, &channel_tags, ct_link) { + e = htsmsg_create_map(); + htsmsg_add_str(e, "key", idnode_uuid_as_str(&ct->ct_id)); + htsmsg_add_str(e, "val", ct->ct_name); + htsmsg_add_msg(l, NULL, e); } + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", l); + return 0; +} + +static void +api_channel_tag_grid + ( idnode_set_t *ins, api_idnode_grid_conf_t *conf ) +{ + channel_tag_t *ct; + + TAILQ_FOREACH(ct, &channel_tags, ct_link) + idnode_set_add(ins, (idnode_t*)ct, &conf->filter); +} + +static int +api_channel_tag_create + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + htsmsg_t *conf; + channel_tag_t *ct; + + if (!(conf = htsmsg_get_map(args, "conf"))) + return EINVAL; + + pthread_mutex_lock(&global_lock); + ct = channel_tag_create(NULL, conf); + if (ct) + channel_tag_save(ct); + pthread_mutex_unlock(&global_lock); + return 0; } @@ -110,7 +133,10 @@ void api_channel_init ( void ) { "channel/list", ACCESS_ANONYMOUS, api_channel_list, NULL }, { "channel/create", ACCESS_ADMIN, api_channel_create, NULL }, - { "channeltag/list", ACCESS_ANONYMOUS, api_channeltag_enum, NULL }, + { "channeltag/class",ACCESS_ANONYMOUS, api_idnode_class, (void*)&channel_tag_class }, + { "channeltag/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_channel_tag_grid }, + { "channeltag/list", ACCESS_ANONYMOUS, api_channel_tag_list, NULL }, + { "channeltag/create", ACCESS_ADMIN, api_channel_tag_create, NULL }, { NULL }, }; diff --git a/src/channels.c b/src/channels.c index 3444b5bf..94a1f515 100644 --- a/src/channels.c +++ b/src/channels.c @@ -45,11 +45,9 @@ struct channel_tree channels; struct channel_tag_queue channel_tags; -static dtable_t *channeltags_dtable; static void channel_tag_init ( void ); static void channel_tag_done ( 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); static void channel_tag_destroy(channel_tag_t *ct, int delconf); @@ -138,7 +136,7 @@ channel_class_tags_get ( void *obj ) /* Add all */ LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) - htsmsg_add_u32(m, NULL, ctm->ctm_tag->ct_identifier); + htsmsg_add_str(m, NULL, idnode_uuid_as_str(&ctm->ctm_tag->ct_id)); return m; } @@ -488,7 +486,7 @@ int channel_set_tags_by_list ( channel_t *ch, htsmsg_t *tags ) { int save = 0; - uint32_t u32; + const char *uuid; channel_tag_mapping_t *ctm, *n; channel_tag_t *ct; htsmsg_field_t *f; @@ -499,8 +497,8 @@ channel_set_tags_by_list ( channel_t *ch, htsmsg_t *tags ) /* Link */ HTSMSG_FOREACH(f, tags) - if (!htsmsg_field_get_u32(f, &u32)) { - if ((ct = channel_tag_find_by_identifier(u32))) + if ((uuid = htsmsg_field_get_str(f)) != NULL) { + if ((ct = channel_tag_find_by_uuid(uuid))) save |= channel_tag_map(ch, ct); } @@ -757,37 +755,30 @@ channel_tag_mapping_destroy(channel_tag_mapping_t *ctm, int flags) /** * */ -static channel_tag_t * -channel_tag_find(const char *id, int create) +channel_tag_t * +channel_tag_create(const char *uuid, htsmsg_t *conf) { channel_tag_t *ct; - char buf[20]; - static int tally; - uint32_t u32; - - if(id != NULL) { - u32 = atoi(id); - TAILQ_FOREACH(ct, &channel_tags, ct_link) - if(ct->ct_identifier == u32) - 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 = MAX(atoi(id), tally); + + if (idnode_insert(&ct->ct_id, uuid, &channel_tag_class, IDNODE_SHORT_UUID)) { + if (uuid) + tvherror("channel", "invalid tag uuid '%s'", uuid); + free(ct); + return NULL; } - ct->ct_identifier = atoi(id); - ct->ct_name = strdup("New tag"); - ct->ct_comment = strdup(""); - ct->ct_icon = strdup(""); + if (conf) + idnode_load(&ct->ct_id, conf); + + if (ct->ct_name == NULL) + ct->ct_name = strdup("New tag"); + if (ct->ct_comment == NULL) + ct->ct_comment = strdup(""); + if (ct->ct_icon == NULL) + ct->ct_icon = strdup(""); + TAILQ_INSERT_TAIL(&channel_tags, ct, ct_link); return ct; } @@ -807,164 +798,104 @@ channel_tag_destroy(channel_tag_t *ct, int delconf) channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_CHANNEL); channel_save(ch); } + hts_settings_remove("channeltags/%s", idnode_uuid_as_str(&ct->ct_id)); } if(ct->ct_enabled && !ct->ct_internal) htsp_tag_delete(ct); + TAILQ_REMOVE(&channel_tags, ct, ct_link); + idnode_unlink(&ct->ct_id); + free(ct->ct_name); free(ct->ct_comment); free(ct->ct_icon); - TAILQ_REMOVE(&channel_tags, ct, ct_link); free(ct); } - /** * */ -static htsmsg_t * -channel_tag_record_build(channel_tag_t *ct) +void +channel_tag_save(channel_tag_t *ct) { - htsmsg_t *e = htsmsg_create_map(); - htsmsg_add_u32(e, "enabled", !!ct->ct_enabled); - htsmsg_add_u32(e, "internal", !!ct->ct_internal); - htsmsg_add_u32(e, "titledIcon", !!ct->ct_titled_icon); - - htsmsg_add_str(e, "name", ct->ct_name); - htsmsg_add_str(e, "comment", ct->ct_comment); - htsmsg_add_str(e, "icon", ct->ct_icon); - htsmsg_add_u32(e, "id", ct->ct_identifier); - return e; + htsmsg_t *c = htsmsg_create_map(); + idnode_save(&ct->ct_id, c); + hts_settings_save(c, "channeltags/%s", idnode_uuid_as_str(&ct->ct_id)); + htsmsg_destroy(c); } -/** - * - */ -static htsmsg_t * -channel_tag_record_get_all(void *opaque) +/* ************************************************************************** + * Channel Tag Class definition + * **************************************************************************/ + +static void +channel_tag_class_save(idnode_t *self) { - htsmsg_t *r = htsmsg_create_list(); - channel_tag_t *ct; - - TAILQ_FOREACH(ct, &channel_tags, ct_link) - htsmsg_add_msg(r, NULL, channel_tag_record_build(ct)); - - return r; + channel_tag_save((channel_tag_t *)self); } - -/** - * - */ -static htsmsg_t * -channel_tag_record_get(void *opaque, const char *id) +static void +channel_tag_class_delete(idnode_t *self) { - channel_tag_t *ct; - - if((ct = channel_tag_find(id, 0)) == NULL) - return NULL; - return channel_tag_record_build(ct); + channel_tag_destroy((channel_tag_t *)self, 1); } - -/** - * - */ -static htsmsg_t * -channel_tag_record_create(void *opaque) +static const char * +channel_tag_class_get_title (idnode_t *self) { - return channel_tag_record_build(channel_tag_find(NULL, 1)); + channel_tag_t *ct = (channel_tag_t *)self; + return ct->ct_name ?: ""; } - -/** - * - */ -static htsmsg_t * -channel_tag_record_update(void *opaque, const char *id, htsmsg_t *values, - int maycreate) -{ - channel_tag_t *ct; - uint32_t u32; - int was_exposed, is_exposed; - channel_tag_mapping_t *ctm; - - 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")); - tvh_str_update(&ct->ct_icon, htsmsg_get_str(values, "icon")); - - if(!htsmsg_get_u32(values, "titledIcon", &u32)) - ct->ct_titled_icon = u32; - - was_exposed = ct->ct_enabled && !ct->ct_internal; - - if(!htsmsg_get_u32(values, "enabled", &u32)) - ct->ct_enabled = u32; - - if(!htsmsg_get_u32(values, "internal", &u32)) - ct->ct_internal = u32; - - is_exposed = ct->ct_enabled && !ct->ct_internal; - - /* We only export tags to HTSP if enabled == true and internal == false, - thus, it's not as simple as just sending updates here. - Depending on how the flags transition we add update or delete tags */ - - if(was_exposed == 0 && is_exposed == 1) { - htsp_tag_add(ct); - - LIST_FOREACH(ctm, &ct->ct_ctms, ctm_tag_link) - htsp_channel_update(ctm->ctm_channel); - - } else if(was_exposed == 1 && is_exposed == 1) - htsp_tag_update(ct); - else if(was_exposed == 1 && is_exposed == 0) { - - LIST_FOREACH(ctm, &ct->ct_ctms, ctm_tag_link) - htsp_channel_update(ctm->ctm_channel); - - htsp_tag_delete(ct); +const idclass_t channel_tag_class = { + .ic_class = "channeltag", + .ic_caption = "Channel Tag", + .ic_save = channel_tag_class_save, + .ic_get_title = channel_tag_class_get_title, + .ic_delete = channel_tag_class_delete, + .ic_properties = (const property_t[]) { + { + .type = PT_BOOL, + .id = "enabled", + .name = "Enabled", + .off = offsetof(channel_tag_t, ct_enabled), + }, + { + .type = PT_STR, + .id = "name", + .name = "Name", + .off = offsetof(channel_tag_t, ct_name), + }, + { + .type = PT_BOOL, + .id = "internal", + .name = "Internal", + .off = offsetof(channel_tag_t, ct_internal), + }, + { + .type = PT_STR, + .id = "icon", + .name = "Icon (full URL)", + .off = offsetof(channel_tag_t, ct_icon), + }, + { + .type = PT_BOOL, + .id = "titled_icon", + .name = "Icon has title", + .off = offsetof(channel_tag_t, ct_titled_icon), + }, + { + .type = PT_STR, + .id = "comment", + .name = "Comment", + .off = offsetof(channel_tag_t, ct_comment), + }, + {} } - 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, 1); - 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, - .dtc_read_access = ACCESS_ADMIN, - .dtc_write_access = ACCESS_ADMIN, - .dtc_mutex = &global_lock, }; - - /** * */ @@ -972,7 +903,6 @@ channel_tag_t * channel_tag_find_by_name(const char *name, int create) { channel_tag_t *ct; - char str[50]; TAILQ_FOREACH(ct, &channel_tags, ct_link) if(!strcasecmp(ct->ct_name, name)) @@ -981,14 +911,11 @@ channel_tag_find_by_name(const char *name, int create) if(!create) return NULL; - ct = channel_tag_find(NULL, 1); + ct = channel_tag_create(NULL, NULL); ct->ct_enabled = 1; tvh_str_update(&ct->ct_name, name); - snprintf(str, sizeof(str), "%d", ct->ct_identifier); - dtable_record_store(channeltags_dtable, str, channel_tag_record_build(ct)); - - dtable_store_changed(channeltags_dtable); + channel_tag_save(ct); return ct; } @@ -1001,19 +928,31 @@ channel_tag_find_by_identifier(uint32_t id) { channel_tag_t *ct; TAILQ_FOREACH(ct, &channel_tags, ct_link) { - if(ct->ct_identifier == id) + if(idnode_get_short_uuid(&ct->ct_id) == id) return ct; } return NULL; } +/** + * Init / Done + */ + static void channel_tag_init ( void ) { + htsmsg_t *c, *m; + htsmsg_field_t *f; + TAILQ_INIT(&channel_tags); - channeltags_dtable = dtable_create(&channel_tags_dtc, "channeltags", NULL); - dtable_load(channeltags_dtable); + if ((c = hts_settings_load_r(1, "channeltags")) != NULL) { + HTSMSG_FOREACH(f, c) { + if (!(m = htsmsg_field_get_map(f))) continue; + (void)channel_tag_create(f->hmf_name, m); + } + htsmsg_destroy(c); + } } static void @@ -1025,5 +964,4 @@ channel_tag_done ( void ) while ((ct = TAILQ_FIRST(&channel_tags)) != NULL) channel_tag_destroy(ct, 0); pthread_mutex_unlock(&global_lock); - dtable_delete("channeltags"); } diff --git a/src/channels.h b/src/channels.h index 8cb66d79..4b5fd80c 100644 --- a/src/channels.h +++ b/src/channels.h @@ -79,17 +79,22 @@ typedef struct channel * Channel tag */ typedef struct channel_tag { + + idnode_t ct_id; + TAILQ_ENTRY(channel_tag) ct_link; + int ct_enabled; int ct_internal; int ct_titled_icon; - int ct_identifier; char *ct_name; char *ct_comment; char *ct_icon; + struct channel_tag_mapping_list ct_ctms; struct dvr_autorec_entry_list ct_autorecs; + } channel_tag_t; /** @@ -120,6 +125,7 @@ typedef struct channel_service_mapping { } channel_service_mapping_t; extern const idclass_t channel_class; +extern const idclass_t channel_tag_class; void channel_init(void); void channel_done(void); @@ -145,10 +151,17 @@ channel_t *channel_find_by_number(int no); int channel_set_tags_by_list ( channel_t *ch, htsmsg_t *tags ); int channel_set_services_by_list ( channel_t *ch, htsmsg_t *svcs ); +channel_tag_t *channel_tag_create(const char *uuid, htsmsg_t *conf); + channel_tag_t *channel_tag_find_by_name(const char *name, int create); channel_tag_t *channel_tag_find_by_identifier(uint32_t id); +static inline channel_tag_t *channel_tag_find_by_uuid(const char *uuid) + { return (channel_tag_t*)idnode_find(uuid, &channel_tag_class); } + +void channel_tag_save(channel_tag_t *ct); + int channel_access(channel_t *ch, struct access *a, const char *username); int channel_tag_map(channel_t *ch, channel_tag_t *ct); diff --git a/src/epg.c b/src/epg.c index 413d229c..02e01bcc 100644 --- a/src/epg.c +++ b/src/epg.c @@ -2287,7 +2287,7 @@ void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, epg_genre_t *genre, const char *title, const char *lang, int min_duration, int max_duration) { channel_t *ch = channel ? channel_find(channel) : NULL; - channel_tag_t *ct = tag ? channel_tag_find_by_name(tag, 0) : NULL; + channel_tag_t *ct = tag ? channel_tag_find_by_uuid(tag) : NULL; epg_query0(eqr, ch, ct, genre, title, lang, min_duration, max_duration); } diff --git a/src/htsp_server.c b/src/htsp_server.c index e84c22b8..942b5301 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -575,7 +575,7 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp) LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) { ct = ctm->ctm_tag; if(ct->ct_enabled && !ct->ct_internal) - htsmsg_add_u32(tags, NULL, ct->ct_identifier); + htsmsg_add_u32(tags, NULL, idnode_get_short_uuid(&ct->ct_id)); } LIST_FOREACH(csm, &ch->ch_services, csm_chn_link) { @@ -607,7 +607,7 @@ htsp_build_tag(channel_tag_t *ct, const char *method, int include_channels) htsmsg_t *out = htsmsg_create_map(); htsmsg_t *members = include_channels ? htsmsg_create_list() : NULL; - htsmsg_add_u32(out, "tagId", ct->ct_identifier); + htsmsg_add_u32(out, "tagId", idnode_get_short_uuid(&ct->ct_id)); htsmsg_add_str(out, "tagName", ct->ct_name); htsmsg_add_str(out, "tagIcon", ct->ct_icon); @@ -2461,7 +2461,7 @@ void htsp_tag_delete(channel_tag_t *ct) { htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_u32(m, "tagId", ct->ct_identifier); + htsmsg_add_u32(m, "tagId", idnode_get_short_uuid(&ct->ct_id)); htsmsg_add_str(m, "method", "tagDelete"); htsp_async_send(m, HTSP_ASYNC_ON); } diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 2e665d72..1b4e30e1 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -502,50 +502,6 @@ extjs_epggrab(http_connection_t *hc, const char *remain, void *opaque) return 0; } -/** - * - */ -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(op != NULL && !strcmp(op, "listTags")) { - - out = htsmsg_create_map(); - array = htsmsg_create_list(); - - TAILQ_FOREACH(ct, &channel_tags, ct_link) { - if(!ct->ct_enabled) - continue; - - e = htsmsg_create_map(); - htsmsg_add_u32(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; - -} - /** * */ @@ -1752,7 +1708,6 @@ extjs_start(void) http_path_add("/capabilities", NULL, extjs_capabilities, ACCESS_WEB_INTERFACE); http_path_add("/tablemgr", NULL, extjs_tablemgr, 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); http_path_add("/epg", NULL, extjs_epg, ACCESS_WEB_INTERFACE); http_path_add("/epgrelated", NULL, extjs_epgrelated, ACCESS_WEB_INTERFACE); diff --git a/src/webui/static/app/chconf.js b/src/webui/static/app/chconf.js index c9ec534d..f25baa91 100644 --- a/src/webui/static/app/chconf.js +++ b/src/webui/static/app/chconf.js @@ -2,26 +2,25 @@ * Channel tags */ insertChannelTagsClearOption = function( scope, records, options ){ - var placeholder = Ext.data.Record.create(['identifier', 'name']); - scope.insert(0,new placeholder({identifier: '-1', name: '(Clear filter)'})); + var placeholder = Ext.data.Record.create(['key', 'val']); + scope.insert(0,new placeholder({key: '-1', val: '(Clear filter)'})); }; tvheadend.channelTags = new Ext.data.JsonStore({ - autoLoad: true, + url: 'api/channeltag/list', root: 'entries', - fields: ['identifier', 'name'], - id: 'identifier', - url: 'channeltags', - baseParams: { - op: 'listTags' + fields: ['key', 'val'], + id: 'key', + autoLoad: true, + sortInfo: { + field: 'val', + direction: 'ASC', }, listeners: { 'load': insertChannelTagsClearOption } }); -tvheadend.channelTags.setDefaultSort('name', 'ASC'); - tvheadend.comet.on('channeltags', function(m) { if (m.reload != null) tvheadend.channelTags.reload(); @@ -59,7 +58,7 @@ tvheadend.comet.on('channels', function(m) { tvheadend.channels.reload(); }); -tvheadend.channel_tab = function(panel) +tvheadend.channel_tab = function(panel, index) { function assign_low_number() { var tab = panel.getActiveTab(); @@ -191,7 +190,7 @@ tvheadend.channel_tab = function(panel) comet: 'channel', titleS: 'Channel', titleP: 'Channels', - tabIndex: 0, + tabIndex: index, help: function() { new tvheadend.help('Channels', 'config_channels.html'); }, diff --git a/src/webui/static/app/cteditor.js b/src/webui/static/app/cteditor.js index 3fc36549..e2843ee8 100644 --- a/src/webui/static/app/cteditor.js +++ b/src/webui/static/app/cteditor.js @@ -1,48 +1,27 @@ -tvheadend.cteditor = function() { - var fm = Ext.form; +/* + * Channel Tag + */ - var cm = new Ext.grid.ColumnModel({ - defaultSortable: true, - columns: [{ - xtype: 'checkcolumn', - header: "Enabled", - dataIndex: 'enabled', - width: 60 - }, { - header: "Name", - dataIndex: 'name', - editor: new fm.TextField({ - allowBlank: false - }) - }, { - xtype: 'checkcolumn', - header: "Internal", - dataIndex: 'internal', - width: 100 - }, { - header: "Icon (full URL)", - dataIndex: 'icon', - width: 400, - editor: new fm.TextField({}) - }, { - xtype: 'checkcolumn', - header: "Icon has title", - dataIndex: 'titledIcon', - width: 100, - tooltip: 'Set this if the supplied icon has a title embedded. ' - + 'This will tell displaying application not to superimpose title ' - + 'on top of logo.' - }, { - header: "Comment", - dataIndex: 'comment', - width: 400, - editor: new fm.TextField({}) - }]}); +tvheadend.cteditor = function(panel, index) +{ + tvheadend.idnode_grid(panel, { + url: 'api/channeltag', + comet: 'channeltag', + titleS: 'Channel Tag', + titleP: 'Channel Tags', + tabIndex: index, + add: { + url: 'api/channeltag', + create: { } + }, + sort: { + field: 'name', + direction: 'ASC' + }, + help: function() { + new tvheadend.help('Channel Tags', 'config_tags.html'); + }, + }); - var ChannelTagRecord = Ext.data.Record.create([ - 'enabled', 'name', 'internal', 'icon', 'comment', 'titledIcon']); - - return new tvheadend.tableEditor('Channel Tags', 'channeltags', cm, - ChannelTagRecord, [], - null, 'config_tags.html', 'tags'); + return panel; }; diff --git a/src/webui/static/app/dvr.js b/src/webui/static/app/dvr.js index cbd83451..35a23e74 100644 --- a/src/webui/static/app/dvr.js +++ b/src/webui/static/app/dvr.js @@ -639,7 +639,7 @@ tvheadend.autoreceditor = function() { header: "Channel tag", dataIndex: 'tag', editor: new Ext.form.ComboBox({ - displayField: 'name', + displayField: 'val', store: tvheadend.channelTags, mode: 'local', editable: true, diff --git a/src/webui/static/app/epg.js b/src/webui/static/app/epg.js index 7b4a6504..5708df16 100644 --- a/src/webui/static/app/epg.js +++ b/src/webui/static/app/epg.js @@ -395,7 +395,7 @@ tvheadend.epg = function() { var epgFilterChannelTags = new Ext.form.ComboBox({ width: 200, - displayField: 'name', + displayField: 'val', store: tvheadend.channelTags, mode: 'local', editable: true, @@ -511,10 +511,10 @@ tvheadend.epg = function() { }); epgFilterChannelTags.on('select', function(c, r) { - if (r.data.identifier == -1) + if (r.data.key == -1) clearChannelTagsFilter(); - else if (epgStore.baseParams.tag !== r.data.name) - epgStore.baseParams.tag = r.data.name; + else if (epgStore.baseParams.tag !== r.data.key) + epgStore.baseParams.tag = r.data.key; epgStore.reload(); }); diff --git a/src/webui/static/app/idnode.js b/src/webui/static/app/idnode.js index 14294e45..0827f5c6 100644 --- a/src/webui/static/app/idnode.js +++ b/src/webui/static/app/idnode.js @@ -196,6 +196,7 @@ tvheadend.IdNodeField = function(conf) t.push(d[i]); } } + t.sort(); v = t.join(','); } return v; diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 6fcb7520..d89c88aa 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -259,12 +259,11 @@ function accessUpdate(o) { autoScroll: true, title: 'Channel / EPG', iconCls: 'television', - items: [ - new tvheadend.cteditor, - new tvheadend.epggrab - ] + items: [] }); - tvheadend.channel_tab(tvheadend.conf_chepg); + tvheadend.channel_tab(tvheadend.conf_chepg, 0); + tvheadend.cteditor(tvheadend.conf_chepg, 1); + tvheadend.conf_chepg.insert(2, new tvheadend.epggrab); tabs1.push(tvheadend.conf_chepg); /* DVR / Timeshift */ diff --git a/src/webui/webui.c b/src/webui/webui.c index 071ac750..8266de95 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -477,7 +477,7 @@ http_tag_list_playlist(http_connection_t *hc) if(!ct->ct_enabled || ct->ct_internal) continue; - snprintf(buf, sizeof(buf), "/playlist/tagid/%d", ct->ct_identifier); + snprintf(buf, sizeof(buf), "/playlist/tagid/%d", idnode_get_short_uuid(&ct->ct_id)); htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ct->ct_name); htsbuf_qprintf(hq, "http://%s%s?ticket=%s", host, buf, access_ticket_create(buf));