channel: tags - move to idnode system

This commit is contained in:
Jaroslav Kysela 2014-08-21 13:20:28 +02:00
parent a958fd749e
commit 3c088cb271
13 changed files with 213 additions and 303 deletions

View file

@ -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 },
};

View file

@ -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");
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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');
},

View file

@ -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;
};

View file

@ -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,

View file

@ -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();
});

View file

@ -196,6 +196,7 @@ tvheadend.IdNodeField = function(conf)
t.push(d[i]);
}
}
t.sort();
v = t.join(',');
}
return v;

View file

@ -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 */

View file

@ -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));