channel: migrated channel_t to an idnode

Also since the channel name is no longer unique various other things have
had to be updated.
This commit is contained in:
Adam Sutton 2013-08-20 16:21:02 +01:00
parent b9c1171a7d
commit 65c304fb9a
32 changed files with 713 additions and 907 deletions

View file

@ -112,6 +112,7 @@ SRCS = src/version.c \
SRCS += \
src/api.c \
src/api/api_idnode.c \
src/api/api_channel.c \
src/api/api_service.c \
src/api/api_mpegts.c \

View file

@ -118,4 +118,5 @@ void api_init ( void )
api_idnode_init();
api_mpegts_init();
api_service_init();
api_channel_init();
}

View file

@ -59,6 +59,7 @@ void api_init ( void );
void api_idnode_init ( void );
void api_mpegts_init ( void );
void api_service_init ( void );
void api_channel_init ( void );
/*
* IDnode
@ -85,4 +86,7 @@ int api_idnode_class
int api_idnode_tree
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
int api_idnode_load_by_class
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp );
#endif /* __TVH_API_H__ */

74
src/api/api_channel.c Normal file
View file

@ -0,0 +1,74 @@
/*
* API - channel related calls
*
* Copyright (C) 2013 Adam Sutton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TVH_API_SERVICE_H__
#define __TVH_API_SERVICE_H__
#include "tvheadend.h"
#include "channels.h"
#include "access.h"
#include "api.h"
// TODO: this will need converting to an idnode system
static int
api_channel_list
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
channel_t *ch;
htsmsg_t *l, *e;
l = htsmsg_create_list();
pthread_mutex_lock(&global_lock);
CHANNEL_FOREACH(ch) {
e = htsmsg_create_map();
htsmsg_add_str(e, "key", idnode_uuid_as_str(&ch->ch_id));
htsmsg_add_str(e, "val", ch->ch_name ?: "");
htsmsg_add_msg(l, NULL, e);
}
pthread_mutex_unlock(&global_lock);
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
return 0;
}
static void
api_channel_grid
( idnode_set_t *ins, api_idnode_grid_conf_t *conf )
{
channel_t *ch;
CHANNEL_FOREACH(ch)
idnode_set_add(ins, (idnode_t*)ch, &conf->filter);
}
void api_channel_init ( void )
{
static api_hook_t ah[] = {
{ "channel/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&channel_class },
{ "channel/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_channel_grid },
{ "channel/list", ACCESS_ANONYMOUS, api_channel_list, NULL },
{ NULL },
};
api_register_all(ah);
}
#endif /* __TVH_API_IDNODE_H__ */

View file

@ -133,9 +133,9 @@ api_idnode_grid
return 0;
}
static int
int
api_idnode_load_by_class
( const char *class, htsmsg_t *args, htsmsg_t **resp )
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
int i, _enum;
const idclass_t *idc;
@ -149,10 +149,8 @@ api_idnode_load_by_class
pthread_mutex_lock(&global_lock);
/* Find class */
if (!(idc = idclass_find(class))) {
pthread_mutex_unlock(&global_lock);
return EINVAL;
}
idc = opaque;
assert(idc);
l = htsmsg_create_list();
if ((is = idnode_find_all(idc))) {
@ -162,7 +160,7 @@ api_idnode_load_by_class
/* Name/UUID only */
if (_enum) {
e = htsmsg_create_map();
htsmsg_add_str(e, "key", idnode_uuid_as_str(in));
htsmsg_add_str(e, "key", idnode_uuid_as_str(in));
htsmsg_add_str(e, "val", idnode_get_title(in));
/* Full record */
@ -192,8 +190,16 @@ api_idnode_load
const char *uuid, *class;
/* Class based */
if ((class = htsmsg_get_str(args, "class")))
return api_idnode_load_by_class(class, args, resp);
if ((class = htsmsg_get_str(args, "class"))) {
const idclass_t *idc;
pthread_mutex_lock(&global_lock);
idc = idclass_find(class);
pthread_mutex_unlock(&global_lock);
if (!idc)
return EINVAL;
// TODO: bit naff that 2 locks are required here
return api_idnode_load_by_class((void*)idc, NULL, args, resp);
}
/* UUIDs */
if (!(f = htsmsg_field_find(args, "uuid")))

View file

@ -21,6 +21,7 @@
#define __TVH_API_SERVICE_H__
#include "tvheadend.h"
#include "service.h"
#include "service_mapper.h"
#include "access.h"
#include "api.h"
@ -75,10 +76,13 @@ api_mapper_status
void api_service_init ( void )
{
extern const idclass_t service_class;
static api_hook_t ah[] = {
{ "service/mapper/start", ACCESS_ADMIN, api_mapper_start, NULL },
{ "service/mapper/stop", ACCESS_ADMIN, api_mapper_stop, NULL },
{ "service/mapper/status", ACCESS_ADMIN, api_mapper_status, NULL },
{ "service/list", ACCESS_ANONYMOUS, api_idnode_load_by_class,
(void*)&service_class },
{ NULL },
};

View file

@ -40,12 +40,14 @@
#include "htsp_server.h"
#include "imagecache.h"
#include "service_mapper.h"
#include "htsbuf.h"
struct channel_tree channels;
struct channel_tree channel_name_tree;
static struct channel_tree channel_identifier_tree;
struct channel_tag_queue channel_tags;
static dtable_t *channeltags_dtable;
static void channel_tag_init ( 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);
@ -53,520 +55,389 @@ static void channel_tag_mapping_destroy(channel_tag_mapping_t *ctm,
#define CTM_DESTROY_UPDATE_TAG 0x1
#define CTM_DESTROY_UPDATE_CHANNEL 0x2
static int
ch_id_cmp ( channel_t *a, channel_t *b )
{
return channel_get_id(a) - channel_get_id(b);
}
/* **************************************************************************
* Class definition
* *************************************************************************/
/**
*
*/
static void
channel_list_changed(void)
channel_class_save ( idnode_t *self )
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_u32(m, "reload", 1);
notify_by_msg("channels", m);
channel_save((channel_t*)self);
}
static int
dictcmp(const char *a, const char *b)
static const void *
channel_class_services_get ( void *obj )
{
long int da, db;
static char *s = NULL;
const char *uuid;
int first = 1;
channel_t *ch = obj;
htsbuf_queue_t hq;
channel_service_mapping_t *csm;
/* Free previous */
if (s) free(s);
s = NULL;
while(1) {
switch((*a >= '0' && *a <= '9' ? 1 : 0)|(*b >= '0' && *b <= '9' ? 2 : 0)) {
case 0: /* 0: a is not a digit, nor is b */
if(*a != *b)
return *(const unsigned char *)a - *(const unsigned char *)b;
if(*a == 0)
return 0;
a++;
b++;
break;
case 1: /* 1: a is a digit, b is not */
case 2: /* 2: a is not a digit, b is */
return *(const unsigned char *)a - *(const unsigned char *)b;
case 3: /* both are digits, switch to integer compare */
da = strtol(a, (char **)&a, 10);
db = strtol(b, (char **)&b, 10);
if(da != db)
return da - db;
break;
}
}
}
/**
*
*/
static int
channelcmp(const channel_t *a, const channel_t *b)
{
return dictcmp(a->ch_name, b->ch_name);
}
/**
*
*/
static int
chidcmp(const channel_t *a, const channel_t *b)
{
return a->ch_id - b->ch_id;
}
/**
*
*/
static void
channel_set_name(channel_t *ch, const char *name)
{
const char *n2;
int l, i;
char *cp, c;
free((void *)ch->ch_name);
free((void *)ch->ch_sname);
ch->ch_name = strdup(name);
l = strlen(name);
ch->ch_sname = cp = malloc(l + 1);
n2 = strdup(name);
for(i = 0; i < strlen(n2); i++) {
c = tolower(n2[i]);
if(isalnum(c))
*cp++ = c;
/* Add all */
LIST_FOREACH(csm, &ch->ch_services, csm_svc_link) {
if (first)
htsbuf_queue_init(&hq, 0);
else
*cp++ = '_';
}
*cp = 0;
free((void *)n2);
RB_INSERT_SORTED(&channel_name_tree, ch, ch_name_link, channelcmp);
//assert(x == NULL);
/* Notify clients */
channel_list_changed();
}
/**
*
*/
static channel_t *
channel_create2(const char *name, int number)
{
channel_t *ch, *x;
int id;
char buf[32];
ch = RB_LAST(&channel_identifier_tree);
if(ch == NULL) {
id = 1;
} else {
id = ch->ch_id + 1;
htsbuf_append(&hq, ",", 1);
uuid = idnode_uuid_as_str(&csm->csm_svc->s_id);
htsbuf_append(&hq, uuid, strlen(uuid));
first = 0;
}
if (!name || !*name) {
snprintf(buf, sizeof(buf), "Channel %d", id);
name = buf;
/* Build string */
if (!first) {
s = htsbuf_to_string(&hq);
htsbuf_queue_flush(&hq);
}
ch = calloc(1, sizeof(channel_t));
channel_set_name(ch, name);
ch->ch_number = number;
ch->ch_id = id;
x = RB_INSERT_SORTED(&channel_identifier_tree, ch,
ch_identifier_link, chidcmp);
assert(x == NULL);
epggrab_channel_add(ch);
htsp_channel_add(ch);
return ch;
return &s;
}
/**
*
*/
channel_t *
channel_create ( const char *name )
static int
channel_class_services_set ( void *obj, const void *p )
{
channel_t *ch = channel_create2(name, 0);
channel_save(ch);
return ch;
return channel_set_services_by_list(obj, p);
}
/**
*
*/
channel_t *
channel_find_by_name(const char *name, int create, int channel_number)
static htsmsg_t *
channel_class_services_enum ( void *obj )
{
lock_assert(&global_lock);
channel_t *ch, skel;
htsmsg_t *e, *m = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "service/list");
htsmsg_add_str(m, "event", "service");
e = htsmsg_create_map();
htsmsg_add_u32(e, "enum", 1);
htsmsg_add_msg(m, "params", e);
return m;
}
if (name) {
skel.ch_name = (char *)name;
ch = RB_FIND(&channel_name_tree, &skel, ch_name_link, channelcmp);
if(ch != NULL || create == 0)
return ch;
static const void *
channel_class_tags_get ( void *obj )
{
static char *s = NULL;
int first = 1;
channel_t *ch = obj;
htsbuf_queue_t hq;
channel_tag_mapping_t *ctm;
/* Free previous */
if (s) free(s);
s = NULL;
/* Add all */
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
if (first)
htsbuf_queue_init(&hq, 0);
else
htsbuf_append(&hq, ",", 1);
htsbuf_append(&hq, ctm->ctm_tag->ct_name, strlen(ctm->ctm_tag->ct_name));
first = 0;
}
return channel_create2(name, channel_number);
/* Build string */
if (!first) {
s = htsbuf_to_string(&hq);
htsbuf_queue_flush(&hq);
}
return &s;
}
/**
*
*/
channel_t *
channel_find_by_identifier(int id)
static int
channel_class_tags_set ( void *obj, const void *p )
{
channel_t skel, *ch;
lock_assert(&global_lock);
skel.ch_id = id;
ch = RB_FIND(&channel_identifier_tree, &skel, ch_identifier_link, chidcmp);
return ch;
return channel_set_tags_by_list(obj, p);
}
static htsmsg_t *
channel_class_tags_enum ( void *obj )
{
channel_tag_t *ct;
htsmsg_t *m = htsmsg_create_list();
TAILQ_FOREACH(ct, &channel_tags, ct_link)
htsmsg_add_str(m, NULL, ct->ct_name);
return m;
}
/**
*
*/
static void
channel_load_one(htsmsg_t *c, int id)
channel_class_icon_notify ( void *obj )
{
channel_t *ch = obj;
if (ch->ch_icon)
imagecache_get_id(ch->ch_icon);
}
static const char *
channel_class_get_title ( idnode_t *self )
{
channel_t *ch = (channel_t*)self;
return ch->ch_name;
}
const idclass_t channel_class = {
.ic_class = "service",
.ic_caption = "Service",
.ic_save = channel_class_save,
.ic_get_title = channel_class_get_title,
.ic_properties = (const property_t[]){
#if 0
{
.type = PT_BOOL,
.id = "enabled",
.name = "Enabled",
.off = offsetof(service_t, s_enabled),
},
#endif
{
.type = PT_STR,
.id = "name",
.name = "Name",
.off = offsetof(channel_t, ch_name),
},
{
.type = PT_INT,
.id = "number",
.name = "Number",
.off = offsetof(channel_t, ch_number),
},
{
.type = PT_STR,
.id = "icon",
.name = "Icon",
.off = offsetof(channel_t, ch_icon),
.notify = channel_class_icon_notify,
},
{
.type = PT_INT,
.id = "dvr_pre_time",
.name = "DVR Pre", // TODO: better text?
.off = offsetof(channel_t, ch_dvr_extra_time_pre),
},
{
.type = PT_INT,
.id = "dvr_pst_time",
.name = "DVR Post", // TODO: better text?
.off = offsetof(channel_t, ch_dvr_extra_time_post),
},
{
.type = PT_STR,
.id = "services",
.name = "Services",
.get = channel_class_services_get,
.set = channel_class_services_set,
.list = channel_class_services_enum,
.opts = PO_MULTI
},
{
.type = PT_STR,
.id = "tags",
.name = "Tags",
.get = channel_class_tags_get,
.set = channel_class_tags_set,
.list = channel_class_tags_enum,
},
{}
}
};
/* **************************************************************************
* Find
* *************************************************************************/
// Note: since channel names are no longer unique this method will simply
// return the first entry encountered, so could be somewhat random
channel_t *
channel_find_by_name ( const char *name )
{
channel_t *ch;
const char *name = htsmsg_get_str(c, "name");
htsmsg_t *tags;
htsmsg_field_t *f;
channel_tag_t *ct;
char buf[32];
if(name == NULL)
return;
ch = calloc(1, sizeof(channel_t));
ch->ch_id = id;
if(RB_INSERT_SORTED(&channel_identifier_tree, ch,
ch_identifier_link, chidcmp)) {
/* ID collision, should not happen unless there is something
wrong in the setting storage */
free(ch);
return;
}
channel_set_name(ch, name);
epggrab_channel_add(ch);
tvh_str_update(&ch->ch_icon, htsmsg_get_str(c, "icon"));
imagecache_get_id(ch->ch_icon);
htsmsg_get_s32(c, "dvr_extra_time_pre", &ch->ch_dvr_extra_time_pre);
htsmsg_get_s32(c, "dvr_extra_time_post", &ch->ch_dvr_extra_time_post);
htsmsg_get_s32(c, "channel_number", &ch->ch_number);
if((tags = htsmsg_get_list(c, "tags")) != NULL) {
HTSMSG_FOREACH(f, tags) {
if(f->hmf_type == HMF_S64) {
snprintf(buf, sizeof(buf), "%" PRId64 , f->hmf_s64);
if((ct = channel_tag_find(buf, 0)) != NULL)
channel_tag_map(ch, ct, 1);
}
}
}
// TODO: load services
CHANNEL_FOREACH(ch)
if (!strcmp(ch->ch_name ?: "", name))
break;
return ch;
}
/**
*
*/
static void
channels_load(void)
channel_t *
channel_find_by_id ( uint32_t i )
{
htsmsg_t *l, *c;
htsmsg_field_t *f;
channel_t skel;
memcpy(skel.ch_id.in_uuid, &i, sizeof(i));
if((l = hts_settings_load("channels")) != NULL) {
HTSMSG_FOREACH(f, l) {
if((c = htsmsg_get_map_by_field(f)) == NULL)
continue;
channel_load_one(c, atoi(f->hmf_name));
}
htsmsg_destroy(l);
}
return RB_FIND(&channels, &skel, ch_link, ch_id_cmp);
}
/* **************************************************************************
* Property updating
* *************************************************************************/
/**
* Write out a config file for a channel
*/
void
channel_save(channel_t *ch)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_t *tags;
channel_tag_mapping_t *ctm;
lock_assert(&global_lock);
htsmsg_add_str(m, "name", ch->ch_name);
if(ch->ch_icon != NULL)
htsmsg_add_str(m, "icon", ch->ch_icon);
tags = htsmsg_create_list();
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link)
htsmsg_add_u32(tags, NULL, ctm->ctm_tag->ct_identifier);
htsmsg_add_msg(m, "tags", tags);
htsmsg_add_u32(m, "dvr_extra_time_pre", ch->ch_dvr_extra_time_pre);
htsmsg_add_u32(m, "dvr_extra_time_post", ch->ch_dvr_extra_time_post);
htsmsg_add_s32(m, "channel_number", ch->ch_number);
/* TODO: save services */
hts_settings_save(m, "channels/%d", ch->ch_id);
htsmsg_destroy(m);
}
/**
* Rename a channel and all tied services
*/
int
channel_rename(channel_t *ch, const char *newname)
channel_set_services_by_list ( channel_t *ch, const char *svcs )
{
dvr_entry_t *de;
int save = 0;
char *tmp, *ret, *tok;
service_t *svc;
channel_service_mapping_t *csm, *n;
lock_assert(&global_lock);
/* Mark all for deletion */
LIST_FOREACH(csm, &ch->ch_services, csm_chn_link)
csm->csm_mark = 1;
if (!newname || !*newname) return 0;
/* Link */
tmp = strdup(svcs);
tok = strtok_r(tmp, ",", &ret);
while (tok) {
if ((svc = service_find(tok)))
save |= service_mapper_link(svc, ch);
tok = strtok_r(NULL, ",", &ret);
}
free(tmp);
if(channel_find_by_name(newname, 0, 0))
return -1;
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" renamed to \"%s\"",
ch->ch_name, newname);
RB_REMOVE(&channel_name_tree, ch, ch_name_link);
channel_set_name(ch, newname);
epggrab_channel_mod(ch);
LIST_FOREACH(de, &ch->ch_dvrs, de_channel_link) {
dvr_entry_save(de);
dvr_entry_notify(de);
/* Remove */
for (csm = LIST_FIRST(&ch->ch_services); csm != NULL; csm = n) {
n = LIST_NEXT(csm, csm_chn_link);
if (csm->csm_mark) {
LIST_REMOVE(csm, csm_chn_link);
LIST_REMOVE(csm, csm_svc_link);
free(csm);
save = 1;
}
}
channel_save(ch);
htsp_channel_update(ch);
return save;
}
int
channel_set_tags_by_list ( channel_t *ch, const char *tags )
{
return 0;
}
/**
* Delete channel
*/
void
channel_delete(channel_t *ch)
/* **************************************************************************
* Creation/Deletion
* *************************************************************************/
channel_t *
channel_create0
( channel_t *ch, const idclass_t *idc, const char *uuid, htsmsg_t *conf,
const char *name )
{
lock_assert(&global_lock);
idnode_insert(&ch->ch_id, uuid, idc);
if (RB_INSERT_SORTED(&channels, ch, ch_link, ch_id_cmp)) {
tvherror("channel", "id collision!");
abort();
}
if (conf)
idnode_load(&ch->ch_id, conf);
/* Override the name */
if (name) {
free(ch->ch_name);
ch->ch_name = strdup(name);
}
return ch;
}
void
channel_delete ( channel_t *ch )
{
channel_service_mapping_t *t;
th_subscription_t *s;
channel_tag_mapping_t *ctm;
channel_service_mapping_t *csm;
lock_assert(&global_lock);
tvhinfo("channel", "%s - deleting", ch->ch_name);
/* Tags */
while((ctm = LIST_FIRST(&ch->ch_ctms)) != NULL)
channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_TAG);
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" deleted",
ch->ch_name);
/* DVR */
autorec_destroy_by_channel(ch);
dvr_destroy_by_channel(ch);
while((t = LIST_FIRST(&ch->ch_services)) != NULL)
service_mapper_unlink(t->csm_svc, ch);
/* Services */
while((csm = LIST_FIRST(&ch->ch_services)) != NULL)
service_mapper_unlink(csm->csm_svc, ch);
/* Subscriptions */
while((s = LIST_FIRST(&ch->ch_subscriptions)) != NULL) {
LIST_REMOVE(s, ths_channel_link);
s->ths_channel = NULL;
}
/* EPG */
#if 0
epggrab_channel_rem(ch);
epg_channel_unlink(ch);
#endif
hts_settings_remove("channels/%d", ch->ch_id);
htsp_channel_delete(ch);
RB_REMOVE(&channel_name_tree, ch, ch_name_link);
RB_REMOVE(&channel_identifier_tree, ch, ch_identifier_link);
/* Settings */
hts_settings_remove("channel/%s", idnode_uuid_as_str(&ch->ch_id));
/* Free memory */
RB_REMOVE(&channels, ch, ch_link);
idnode_unlink(&ch->ch_id);
free(ch->ch_name);
free(ch->ch_sname);
free(ch->ch_icon);
channel_list_changed();
free(ch);
}
/**
* Merge services from channel 'src' to channel 'dst'
*
* Then, destroy the 'src' channel
/*
* Save
*/
void
channel_merge(channel_t *dst, channel_t *src)
channel_save ( channel_t *ch )
{
channel_service_mapping_t *t;
htsmsg_t *c = htsmsg_create_map();
idnode_save(&ch->ch_id, c);
hts_settings_save(c, "channel/%s", idnode_uuid_as_str(&ch->ch_id));
htsmsg_destroy(c);
}
lock_assert(&global_lock);
/**
*
*/
void
channel_init ( void )
{
htsmsg_t *c, *e;
htsmsg_field_t *f;
RB_INIT(&channels);
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" merged into \"%s\"",
src->ch_name, dst->ch_name);
/* Tags */
channel_tag_init();
while((t = LIST_FIRST(&src->ch_services)) != NULL)
service_mapper_link(t->csm_svc, dst);
channel_delete(src);
}
/**
*
*/
void
channel_set_icon(channel_t *ch, const char *icon)
{
lock_assert(&global_lock);
if(ch->ch_icon != NULL && !strcmp(ch->ch_icon, icon))
/* Channels */
if (!(c = hts_settings_load_r(1, "channel")))
return;
free(ch->ch_icon);
ch->ch_icon = strdup(icon);
imagecache_get_id(icon);
channel_save(ch);
htsp_channel_update(ch);
}
/**
* Set the amount of minutes to start before / end after recording on a channel
*/
void
channel_set_epg_postpre_time(channel_t *ch, int pre, int mins)
{
if (mins < -10000 || mins > 10000)
mins = 0;
lock_assert(&global_lock);
tvhlog(LOG_NOTICE, "channels",
"Channel \"%s\" epg %s-time set to %d minutes",
ch->ch_name, pre ? "pre":"post", mins);
if (pre) {
if (ch->ch_dvr_extra_time_pre == mins)
return;
else
ch->ch_dvr_extra_time_pre = mins;
} else {
if (ch->ch_dvr_extra_time_post == mins)
return;
else
ch->ch_dvr_extra_time_post = mins;
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_field_get_map(f))) continue;
(void)channel_create(f->hmf_name, e, NULL);
}
channel_save(ch);
htsp_channel_update(ch);
htsmsg_destroy(c);
}
/**
* Set the channel number
/* ***
* Channel tags TODO
*/
void
channel_set_number(channel_t *ch, int number)
{
if(ch->ch_number == number)
return;
ch->ch_number = number;
channel_save(ch);
htsp_channel_update(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, CTM_DESTROY_UPDATE_TAG |
CTM_DESTROY_UPDATE_CHANNEL);
}
}
if(change)
channel_save(ch);
}
/**
*
@ -883,17 +754,10 @@ channel_tag_find_by_identifier(uint32_t id) {
return NULL;
}
/**
*
*/
void
channels_init(void)
static void
channel_tag_init ( void )
{
TAILQ_INIT(&channel_tags);
channeltags_dtable = dtable_create(&channel_tags_dtc, "channeltags", NULL);
dtable_load(channeltags_dtable);
channels_load();
}

View file

@ -20,51 +20,54 @@
#define CHANNELS_H
#include "epg.h"
#include "idnode.h"
RB_HEAD(channel_tree, channel);
LIST_HEAD(channel_tag_mapping_list, channel_tag_mapping);
TAILQ_HEAD(channel_tag_queue, channel_tag);
extern struct channel_tag_queue channel_tags;
extern struct channel_tree channels;
#define CHANNEL_FOREACH(ch) RB_FOREACH(ch, &channels, ch_link)
/*
* Channel definition
*/
typedef struct channel {
typedef struct channel
{
idnode_t ch_id;
RB_ENTRY(channel) ch_link;
int ch_refcount;
int ch_zombie;
RB_ENTRY(channel) ch_name_link;
/* Channel info */
char *ch_name;
char *ch_sname;
RB_ENTRY(channel) ch_identifier_link;
int ch_id;
int ch_number;
char *ch_icon;
struct channel_tag_mapping_list ch_ctms;
/* Service/subscriptions */
LIST_HEAD(, channel_service_mapping) ch_services;
LIST_HEAD(, th_subscription) ch_subscriptions;
LIST_HEAD(, th_subscription) ch_subscriptions;
/* EPG fields */
epg_broadcast_tree_t ch_epg_schedule;
epg_broadcast_t *ch_epg_now;
epg_broadcast_t *ch_epg_next;
gtimer_t ch_epg_timer;
gtimer_t ch_epg_timer_head;
gtimer_t ch_epg_timer_current;
gtimer_t ch_epg_timer_head;
gtimer_t ch_epg_timer_current;
int ch_dvr_extra_time_pre;
int ch_dvr_extra_time_post;
int ch_number; // User configurable number
char *ch_icon;
/* DVR */
int ch_dvr_extra_time_pre;
int ch_dvr_extra_time_post;
struct dvr_entry_list ch_dvrs;
struct dvr_autorec_entry_list ch_autorecs;
struct channel_tag_mapping_list ch_ctms;
} channel_t;
@ -85,7 +88,6 @@ typedef struct channel_tag {
struct dvr_autorec_entry_list ct_autorecs;
} channel_tag_t;
/**
* Channel tag mapping
*/
@ -100,39 +102,41 @@ typedef struct channel_tag_mapping {
} channel_tag_mapping_t;
/*
* Service mappings
*/
typedef struct channel_service_mapping {
LIST_ENTRY(channel_service_mapping) csm_chn_link;
LIST_ENTRY(channel_service_mapping) csm_svc_link;
struct channel *csm_chn;
struct service *csm_svc;
int csm_mark;
} channel_service_mapping_t;
void channels_init(void);
extern const idclass_t channel_class;
channel_t *channel_create(const char *name);
void channel_init(void);
channel_t *channel_find_by_name(const char *name, int create, int number);
channel_t *channel_find_by_identifier(int id);
void channel_set_teletext_rundown(channel_t *ch, int v);
void channel_settings_write(channel_t *ch);
int channel_rename(channel_t *ch, const char *newname);
channel_t *channel_create0
(channel_t *ch, const idclass_t *idc, const char *uuid, htsmsg_t *conf,
const char *name);
#define channel_create(u, c, n)\
channel_create0(calloc(1, sizeof(channel_t)), &channel_class, u, c, n)
void channel_delete(channel_t *ch);
void channel_merge(channel_t *dst, channel_t *src);
channel_t *channel_find_by_name(const char *name);
#define channel_find_by_uuid(u)\
(channel_t*)idnode_find(NULL, &channel_class)
void channel_set_epg_postpre_time(channel_t *ch, int pre, int mins);
channel_t *channel_find_by_id(uint32_t id);
void channel_set_number(channel_t *ch, int number);
#define channel_find channel_find_by_uuid
void channel_set_icon(channel_t *ch, const char *icon);
void channel_set_tags_from_list(channel_t *ch, const char *maplist);
int channel_set_tags_by_list ( channel_t *ch, const char *tags );
int channel_set_services_by_list ( channel_t *ch, const char *svcs );
channel_tag_t *channel_tag_find_by_name(const char *name, int create);
@ -142,4 +146,8 @@ int channel_tag_map(channel_t *ch, channel_tag_t *ct, int check);
void channel_save(channel_t *ch);
#define channel_get_uuid(ch) idnode_uuid_as_str(&ch->ch_id)
#define channel_get_id(ch) idnode_get_short_uuid((&ch->ch_id))
#endif /* CHANNELS_H */

View file

@ -362,7 +362,7 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values,
LIST_REMOVE(dae, dae_channel_link);
dae->dae_channel = NULL;
}
if((ch = channel_find_by_name(s, 0, 0)) != NULL) {
if((ch = channel_find(s)) != NULL) {
LIST_INSERT_HEAD(&ch->ch_autorecs, dae, dae_channel_link);
dae->dae_channel = ch;
}
@ -553,7 +553,7 @@ dvr_autorec_add(const char *config_name,
const char *creator, const char *comment)
{
channel_t *ch = NULL;
if(channel != NULL) ch = channel_find_by_name(channel, 0, 0);
if(channel != NULL) ch = channel_find(channel);
_dvr_autorec_add(config_name, title, ch, tag, content_type,
NULL, NULL, NULL, 0, NULL, creator, comment);
}
@ -624,7 +624,7 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge)
if (purge)
dvr_autorec_purge_spawns(dae);
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
CHANNEL_FOREACH(ch) {
RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
if(autorec_cmp(dae, e))
dvr_entry_create_by_autorec(e, dae);

View file

@ -519,7 +519,7 @@ static void
dvr_db_load_one(htsmsg_t *c, int id)
{
dvr_entry_t *de;
const char *chname, *s, *creator;
const char *chuuid, *chname, *s, *creator;
channel_t *ch;
uint32_t start, stop, bcid, u32;
int d;
@ -531,9 +531,15 @@ dvr_db_load_one(htsmsg_t *c, int id)
if(htsmsg_get_u32(c, "stop", &stop))
return;
if((chname = htsmsg_get_str(c, "channel")) == NULL)
return;
ch = channel_find_by_name(chname, 0, 0);
chname = htsmsg_get_str(c, "channel_name");
chuuid = htsmsg_get_str(c, "channel");
ch = chuuid ? channel_find(chuuid) : NULL;
/* Backwards compat */
if (!ch && !chname) {
chname = chuuid;
ch = channel_find_by_name(chname);
}
s = htsmsg_get_str(c, "config_name");
cfg = dvr_config_find_by_name_default(s);
@ -648,6 +654,8 @@ dvr_entry_save(dvr_entry_t *de)
lock_assert(&global_lock);
if (de->de_channel)
htsmsg_add_str(m, "channel", channel_get_uuid(de->de_channel));
htsmsg_add_str(m, "channel", DVR_CH_NAME(de));
htsmsg_add_u32(m, "start", de->de_start);
htsmsg_add_u32(m, "stop", de->de_stop);

View file

@ -1721,8 +1721,8 @@ epg_episode_t *epg_broadcast_get_episode
if (!ebc) return NULL;
if (ebc->episode) return ebc->episode;
if (!create) return NULL;
snprintf(uri, sizeof(uri)-1, "tvh://channel-%d/bcast-%u/episode",
ebc->channel->ch_id, ebc->id);
snprintf(uri, sizeof(uri)-1, "tvh://channel-%s/bcast-%u/episode",
idnode_uuid_as_str(&ebc->channel->ch_id), ebc->id);
if ((ee = epg_episode_find_by_uri(uri, 1, save)))
*save |= epg_broadcast_set_episode(ebc, ee, ebc->grabber);
return ee;
@ -1756,7 +1756,7 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
htsmsg_add_s64(m, "stop", broadcast->stop);
htsmsg_add_str(m, "episode", broadcast->episode->uri);
if (broadcast->channel)
htsmsg_add_u32(m, "channel", broadcast->channel->ch_id);
htsmsg_add_str(m, "channel", channel_get_uuid(broadcast->channel));
if (broadcast->dvb_eid)
htsmsg_add_u32(m, "dvb_eid", broadcast->dvb_eid);
if (broadcast->is_widescreen)
@ -1796,7 +1796,7 @@ epg_broadcast_t *epg_broadcast_deserialize
epg_serieslink_t *esl;
lang_str_t *ls;
const char *str;
uint32_t chid, eid, u32;
uint32_t eid, u32;
int64_t start, stop;
if ( htsmsg_get_s64(m, "start", &start) ) return NULL;
@ -1818,9 +1818,8 @@ epg_broadcast_t *epg_broadcast_deserialize
}
/* Get channel */
if ( !htsmsg_get_u32(m, "channel", &chid) ) {
ch = channel_find_by_identifier(chid);
}
if ((str = htsmsg_get_str(m, "channel")))
ch = channel_find(str);
if (!ch) return NULL;
/* Create */
@ -2254,9 +2253,11 @@ void epg_query0
/* All channels */
} else {
#if TODO
RB_FOREACH(channel, &channel_name_tree, ch_name_link) {
_eqr_add_channel(eqr, channel, genre, preg, now, lang);
}
#endif
}
if (preg) regfree(preg);
@ -2266,8 +2267,8 @@ void epg_query0
void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
epg_genre_t *genre, const char *title, const char *lang)
{
channel_t *ch = channel ? channel_find_by_name(channel, 0, 0) : NULL;
channel_tag_t *ct = tag ? channel_tag_find_by_name(tag, 0) : NULL;
channel_t *ch = channel ? channel_find(channel) : NULL;
channel_tag_t *ct = tag ? channel_tag_find_by_name(tag, 0) : NULL;
epg_query0(eqr, ch, ct, genre, title, lang);
}

View file

@ -44,6 +44,7 @@ extern epg_object_tree_t epg_serieslinks;
/*
* Use for v1 databases
*/
#if DEPRECATED
static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats )
{
channel_t *ch;
@ -94,6 +95,7 @@ static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats )
/* Set episode */
save |= epg_broadcast_set_episode(ebc, ee, NULL);
}
#endif
/*
* Process v2 data
@ -207,8 +209,7 @@ void epg_init ( void )
case 2:
_epgdb_v2_process(m, &stats);
break;
default: /* v0/1 */
_epgdb_v1_process(m, &stats);
default:
break;
}
@ -267,8 +268,10 @@ void epg_save ( void *p )
{
int fd;
epg_object_t *eo;
#if 0
epg_broadcast_t *ebc;
channel_t *ch;
#endif
epggrab_stats_t stats;
extern gtimer_t epggrab_save_timer;
@ -300,12 +303,14 @@ void epg_save ( void *p )
stats.seasons.total++;
}
if ( _epg_write_sect(fd, "broadcasts") ) return;
#if 0
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
if (_epg_write(fd, epg_broadcast_serialize(ebc))) return;
stats.broadcasts.total++;
}
}
#endif
/* Stats */
tvhlog(LOG_INFO, "epgdb", "saved");

View file

@ -57,12 +57,14 @@ void epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
ecl = calloc(1, sizeof(epggrab_channel_link_t));
ecl->channel = ch;
LIST_INSERT_HEAD(&ec->channels, ecl, link);
#if 0
if (ec->name && epggrab_channel_rename)
channel_rename(ch, ec->name);
if (ec->icon && epggrab_channel_reicon)
channel_set_icon(ch, ec->icon);
if (ec->number>0 && epggrab_channel_renumber)
channel_set_number(ch, ec->number);
#endif
/* Save */
if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec);
@ -82,6 +84,7 @@ int epggrab_channel_match_and_link ( epggrab_channel_t *ec, channel_t *ch )
int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
{
int save = 0;
#if 0
epggrab_channel_link_t *ecl;
if (!ec || !name) return 0;
if (!ec->name || strcmp(ec->name, name)) {
@ -92,6 +95,7 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
channel_rename(ecl->channel, name);
save = 1;
}
#endif
return save;
}
@ -99,6 +103,7 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
{
int save = 0;
#if 0
epggrab_channel_link_t *ecl;
if (!ec->icon || strcmp(ec->icon, icon) ) {
if (!ec | !icon) return 0;
@ -109,6 +114,7 @@ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
channel_set_icon(ecl->channel, icon);
save = 1;
}
#endif
return save;
}
@ -116,6 +122,7 @@ int epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
{
int save = 0;
#if 0
epggrab_channel_link_t *ecl;
if (!ec || (number <= 0)) return 0;
if (ec->number != number) {
@ -125,12 +132,14 @@ int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
channel_set_number(ecl->channel, number);
save = 1;
}
#endif
return save;
}
/* Channel settings updated */
void epggrab_channel_updated ( epggrab_channel_t *ec )
{
#if 0
channel_t *ch;
if (!ec) return;
@ -141,6 +150,7 @@ void epggrab_channel_updated ( epggrab_channel_t *ec )
/* Save */
if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec);
#endif
}
/* ID comparison */

View file

@ -154,7 +154,7 @@ void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch )
htsmsg_add_str(m, "icon", ch->icon);
LIST_FOREACH(ecl, &ch->channels, link) {
if (!a) a = htsmsg_create_list();
htsmsg_add_u32(a, NULL, ecl->channel->ch_id);
htsmsg_add_u32(a, NULL, channel_get_id(ecl->channel));
}
if (a) htsmsg_add_msg(m, "channels", a);
if (ch->number)
@ -214,7 +214,7 @@ static void _epggrab_module_channel_load
egc->number = u32;
if ((a = htsmsg_get_list(m, "channels"))) {
HTSMSG_FOREACH(f, a) {
if ((ch = channel_find_by_identifier((uint32_t)f->hmf_s64))) {
if ((ch = channel_find_by_id((uint32_t)f->hmf_s64))) {
epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t));
ecl->channel = ch;
LIST_INSERT_HEAD(&egc->channels, ecl, link);
@ -223,7 +223,7 @@ static void _epggrab_module_channel_load
/* Compat with older 3.1 code */
} else if (!htsmsg_get_u32(m, "channel", &u32)) {
if ((ch = channel_find_by_identifier(u32))) {
if ((ch = channel_find_by_id(u32))) {
epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t));
ecl->channel = ch;
LIST_INSERT_HEAD(&egc->channels, ecl, link);

View file

@ -463,7 +463,7 @@ htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp)
htsmsg_t *tags = htsmsg_create_list();
htsmsg_t *services = htsmsg_create_list();
htsmsg_add_u32(out, "channelId", ch->ch_id);
htsmsg_add_u32(out, "channelId", channel_get_id(ch));
htsmsg_add_u32(out, "channelNumber", ch->ch_number);
htsmsg_add_str(out, "channelName", ch->ch_name);
@ -545,7 +545,7 @@ htsp_build_tag(channel_tag_t *ct, const char *method, int include_channels)
if(members != NULL) {
LIST_FOREACH(ctm, &ct->ct_ctms, ctm_tag_link)
htsmsg_add_u32(members, NULL, ctm->ctm_channel->ch_id);
htsmsg_add_u32(members, NULL, channel_get_id(ctm->ctm_channel));
htsmsg_add_msg(out, "members", members);
}
@ -566,7 +566,7 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
htsmsg_add_u32(out, "id", de->de_id);
if (de->de_channel)
htsmsg_add_u32(out, "channel", de->de_channel->ch_id);
htsmsg_add_u32(out, "channel", channel_get_id(de->de_channel));
htsmsg_add_s64(out, "start", de->de_start);
htsmsg_add_s64(out, "stop", de->de_stop);
@ -647,7 +647,7 @@ htsp_build_event
htsmsg_add_str(out, "method", method);
htsmsg_add_u32(out, "eventId", e->id);
htsmsg_add_u32(out, "channelId", e->channel->ch_id);
htsmsg_add_u32(out, "channelId", channel_get_id(e->channel));
htsmsg_add_s64(out, "start", e->start);
htsmsg_add_s64(out, "stop", e->stop);
if ((str = epg_broadcast_get_title(e, lang)))
@ -846,7 +846,7 @@ htsp_method_async(htsp_connection_t *htsp, htsmsg_t *in)
htsp_send_message(htsp, htsp_build_tag(ct, "tagAdd", 0), NULL);
/* Send all channels */
RB_FOREACH(ch, &channel_name_tree, ch_name_link)
CHANNEL_FOREACH(ch)
htsp_send_message(htsp, htsp_build_channel(ch, "channelAdd", htsp), NULL);
/* Send all enabled and external tags (now with channel mappings) */
@ -860,7 +860,7 @@ htsp_method_async(htsp_connection_t *htsp, htsmsg_t *in)
/* Send EPG updates */
if (epg) {
RB_FOREACH(ch, &channel_name_tree, ch_name_link)
CHANNEL_FOREACH(ch)
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
if (epgMaxTime && ebc->start > epgMaxTime) break;
htsmsg_t *e = htsp_build_event(ebc, "eventAdd", lang, lastUpdate, htsp);
@ -915,7 +915,7 @@ htsp_method_getEvents(htsp_connection_t *htsp, htsmsg_t *in)
/* Optional fields */
if (!htsmsg_get_u32(in, "channelId", &u32))
if (!(ch = channel_find_by_identifier(u32)))
if (!(ch = channel_find_by_id(u32)))
return htsp_error("Channel does not exist");
if (!htsmsg_get_u32(in, "eventId", &u32))
if (!(e = epg_broadcast_find_by_id(u32, ch)))
@ -944,7 +944,7 @@ htsp_method_getEvents(htsp_connection_t *htsp, htsmsg_t *in)
/* All channels */
} else {
events = htsmsg_create_list();
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
CHANNEL_FOREACH(ch) {
int num = numFollowing;
RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
if (maxTime && e->start > maxTime) break;
@ -984,7 +984,7 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
/* Optional */
if(!(htsmsg_get_u32(in, "channelId", &u32)))
if (!(ch = channel_find_by_identifier(u32)))
if (!(ch = channel_find_by_id(u32)))
return htsp_error("Channel does not exist");
if(!(htsmsg_get_u32(in, "tagId", &u32)))
if (!(ct = channel_tag_find_by_identifier(u32)))
@ -1072,7 +1072,7 @@ htsp_method_addDvrEntry(htsp_connection_t *htsp, htsmsg_t *in)
if(htsmsg_get_s64(in, "stopExtra", &stop_extra))
stop_extra = 0;
if(!htsmsg_get_u32(in, "channelId", &u32))
ch = channel_find_by_identifier(u32);
ch = channel_find_by_id(u32);
if(!htsmsg_get_u32(in, "eventId", &eventid))
e = epg_broadcast_find_by_id(eventid, ch);
if(htsmsg_get_u32(in, "priority", &priority))
@ -1256,18 +1256,18 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in)
#if ENABLE_TIMESHIFT
uint32_t timeshiftPeriod = 0;
#endif
const char *str;
channel_t *ch;
htsp_subscription_t *hs;
const char *str;
if(htsmsg_get_u32(in, "subscriptionId", &sid))
return htsp_error("Missing argument 'subscriptionId'");
if(!htsmsg_get_u32(in, "channelId", &chid)) {
if((ch = channel_find_by_identifier(chid)) == NULL)
if((ch = channel_find_by_id(chid)) == NULL)
return htsp_error("Requested channel does not exist");
} else if((str = htsmsg_get_str(in, "channelName")) != NULL) {
if((ch = channel_find_by_name(str, 0, 0)) == NULL)
if((ch = channel_find_by_name(str)) == NULL)
return htsp_error("Requested channel does not exist");
} else {
@ -2043,7 +2043,7 @@ htsp_channel_update_current(channel_t *ch)
m = htsmsg_create_map();
htsmsg_add_str(m, "method", "channelUpdate");
htsmsg_add_u32(m, "channelId", ch->ch_id);
htsmsg_add_u32(m, "channelId", channel_get_id(ch));
now = ch->ch_epg_now;
next = ch->ch_epg_next;
@ -2086,7 +2086,7 @@ void
htsp_channel_delete(channel_t *ch)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_u32(m, "channelId", ch->ch_id);
htsmsg_add_u32(m, "channelId", channel_get_id(ch));
htsmsg_add_str(m, "method", "channelDelete");
htsp_async_send(m, HTSP_ASYNC_ON);
}

View file

@ -206,6 +206,14 @@ idnode_delete(idnode_t *in)
* Info
* *************************************************************************/
uint32_t
idnode_get_short_uuid (const idnode_t *in)
{
uint32_t u32;
memcpy(&u32, in->in_uuid, sizeof(u32));
return u32;
}
/**
*
*/

View file

@ -110,6 +110,7 @@ void idnode_init(void);
int idnode_insert(idnode_t *in, const char *uuid, const idclass_t *idc);
void idnode_unlink(idnode_t *in);
uint32_t idnode_get_short_uuid (const idnode_t *in);
const char *idnode_uuid_as_str (const idnode_t *in);
idnode_set_t *idnode_get_childs (idnode_t *in);
const char *idnode_get_title (idnode_t *in);

View file

@ -49,7 +49,7 @@ tsfile_network_create_service
if (s) {
char buf[128];
sprintf(buf, "channel-%d", t);
channel_t *c = channel_find_by_name(buf, 1, t);
channel_t *c = channel_create(NULL, NULL, buf);
service_mapper_link((service_t*)s, c);
t++;
}

View file

@ -701,7 +701,21 @@ main(int argc, char **argv)
service_init();
channels_init();
#if ENABLE_TSFILE
if(opt_tsfile.num) {
tsfile_init(opt_tsfile_tuner ?: opt_tsfile.num);
for (i = 0; i < opt_tsfile.num; i++)
tsfile_add_file(opt_tsfile.str[i]);
}
#endif
#if ENABLE_IPTV
iptv_init();
#endif
#if ENABLE_LINUXDVB
linuxdvb_init(adapter_mask);
#endif
channel_init();
subscription_init();
@ -726,19 +740,6 @@ main(int argc, char **argv)
htsp_init(opt_bindaddr);
#if ENABLE_TSFILE
if(opt_tsfile.num) {
tsfile_init(opt_tsfile_tuner ?: opt_tsfile.num);
for (i = 0; i < opt_tsfile.num; i++)
tsfile_add_file(opt_tsfile.str[i]);
}
#endif
#if ENABLE_IPTV
iptv_init();
#endif
#if ENABLE_LINUXDVB
linuxdvb_init(adapter_mask);
#endif
if(opt_subscribe != NULL)
subscription_dummy_join(opt_subscribe, 1);

View file

@ -246,6 +246,8 @@ prop_serialize(void *obj, const property_t *pl, htsmsg_t *msg, int optmask, htsm
htsmsg_add_u32(m, "nosave", 1);
if (pl->opts & PO_WRONCE)
htsmsg_add_u32(m, "wronce", 1);
if (pl->opts & PO_MULTI)
htsmsg_add_u32(m, "multi", 1);
/* Enum list */
if (pl->list)

View file

@ -43,6 +43,7 @@ typedef enum {
#define PO_RDONLY 0x01 // Property is read-only
#define PO_NOSAVE 0x02 // Property is transient (not saved)
#define PO_WRONCE 0x04 // Property is write-once (i.e. on creation)
#define PO_MULTI 0x08 // Multi-select
/*
* Property definition

View file

@ -49,24 +49,101 @@ static void service_class_save(struct idnode *self);
struct service_queue service_all;
#if 0
static const void *service_class_channel_get(void *obj);
static int service_class_channel_set(void *obj, const void *str);
static htsmsg_t *service_class_channel_enum(void *p)
static const void *
service_class_channel_get ( void *obj )
{
channel_t *ch;
htsmsg_t *list = htsmsg_create_list();
RB_FOREACH(ch, &channel_name_tree, ch_name_link)
if (ch->ch_name)
htsmsg_add_str(list, NULL, ch->ch_name);
return list;
service_t *svc = obj;
channel_service_mapping_t *csm;
static char buf[2048], *s;
// TODO: make this dynamic length
int first = 1;
*buf = 0;
LIST_FOREACH(csm, &svc->s_channels, csm_chn_link) {
if (!first)
strcat(buf, ",");
strcat(buf, idnode_uuid_as_str(&csm->csm_chn->ch_id));
first = 0;
}
s = first ? NULL : buf;
return &s;
}
static int
service_class_channel_set
( void *obj, const void *str )
{
int save = 0;
service_t *svc = obj;
char *tmp, *tok, *sptr;
channel_t *ch;
channel_service_mapping_t *csm, *n;
/* Mark all for deletion */
LIST_FOREACH(csm, &svc->s_channels, csm_chn_link)
csm->csm_mark = 1;
/* Make new links */
tmp = strdup(str);
tok = strtok_r(tmp, ",", &sptr);
while (tok) {
ch = channel_find(tok);
if (ch) {
save |= service_mapper_link(svc, ch);
}
tok = strtok_r(NULL, ",", &sptr);
}
/* Delete unlinked */
for (csm = LIST_FIRST(&svc->s_channels); csm != NULL; csm = n ) {
n = LIST_NEXT(csm, csm_chn_link);
if (csm->csm_mark) {
save = 1;
LIST_REMOVE(csm, csm_chn_link);
LIST_REMOVE(csm, csm_svc_link);
free(csm);
}
}
return save;
}
static htsmsg_t *
service_class_channel_enum
( void *obj )
{
htsmsg_t *p, *m = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "channel/list");
htsmsg_add_str(m, "event", "channel");
p = htsmsg_create_map();
htsmsg_add_u32(p, "enum", 1);
htsmsg_add_msg(m, "params", p);
return m;
}
static const char *
service_class_get_title ( idnode_t *self )
{
static char *ret = NULL;
service_t *s = (service_t*)self;
if (ret) {
free(ret);
ret = NULL;
}
pthread_mutex_lock(&s->s_stream_mutex);
if (s->s_nicename)
ret = strdup(s->s_nicename);
pthread_mutex_unlock(&s->s_stream_mutex);
return ret;
}
#endif
const idclass_t service_class = {
.ic_class = "service",
.ic_caption = "Service",
.ic_save = service_class_save,
.ic_get_title = service_class_get_title,
.ic_properties = (const property_t[]){
{
.type = PT_BOOL,
@ -74,16 +151,15 @@ const idclass_t service_class = {
.name = "Enabled",
.off = offsetof(service_t, s_enabled),
},
#if 0
{
.type = PT_STR,
.id = "channel",
.name = "Channel",
.get = service_class_channel_get,
.set = service_class_channel_set,
.list = service_class_channel_enum
.list = service_class_channel_enum,
.opts = PO_MULTI | PO_NOSAVE
},
#endif
{}
}
};
@ -451,7 +527,7 @@ service_create0
* Find a service based on the given identifier
*/
service_t *
service_find_by_identifier(const char *identifier)
service_find(const char *identifier)
{
return idnode_find(identifier, &service_class);
}
@ -650,15 +726,18 @@ service_is_hdtv(service_t *t)
int
service_is_radio(service_t *t)
{
int ret = 0;
if (t->s_servicetype == ST_RADIO)
return 1;
else if (t->s_servicetype == ST_NONE) {
elementary_stream_t *st;
TAILQ_FOREACH(st, &t->s_components, es_link)
if (SCT_ISAUDIO(st->es_type))
return 1;
if (SCT_ISVIDEO(st->es_type))
return 0;
else if (SCT_ISAUDIO(st->es_type))
ret = 1;
}
return 0;
return ret;
}
/**

View file

@ -27,6 +27,8 @@ extern const idclass_t service_class;
extern struct service_queue service_all;
struct channel;
/**
* Stream, one media component for a service.
*/
@ -232,6 +234,7 @@ typedef struct service {
*/
enum {
ST_NONE,
ST_OTHER,
ST_SDTV,
ST_HDTV,
ST_RADIO
@ -433,7 +436,8 @@ void service_unref(service_t *t);
void service_ref(service_t *t);
service_t *service_find_by_identifier(const char *identifier);
service_t *service_find(const char *identifier);
#define service_find_by_identifier service_find
service_instance_t *service_find_instance(struct service *s,
struct channel *ch,
@ -455,6 +459,7 @@ const char *service_servicetype_txt(service_t *t);
int service_is_sdtv(service_t *t);
int service_is_hdtv(service_t *t);
int service_is_radio(service_t *t);
int service_is_other(service_t *t);
#define service_is_tv(s) (service_is_hdtv(s) || service_is_sdtv(s))
int service_is_encrypted ( service_t *t );

View file

@ -141,15 +141,17 @@ service_mapper_remove ( service_t *s )
/*
* Link service and channel
*/
void
int
service_mapper_link ( service_t *s, channel_t *c )
{
channel_service_mapping_t *csm;
/* Already linked */
LIST_FOREACH(csm, &s->s_channels, csm_chn_link)
if (csm->csm_chn == c)
return;
if (csm->csm_chn == c) {
csm->csm_mark = 0;
return 0;
}
/* Link */
csm = calloc(1, sizeof(channel_service_mapping_t));
@ -157,6 +159,7 @@ service_mapper_link ( service_t *s, channel_t *c )
csm->csm_svc = s;
LIST_INSERT_HEAD(&s->s_channels, csm, csm_svc_link);
LIST_INSERT_HEAD(&c->ch_services, csm, csm_chn_link);
return 1;
}
void
@ -182,7 +185,7 @@ void
service_mapper_process ( service_t *s )
{
int num;
channel_t *chn;
channel_t *chn = NULL;
/* Ignore */
if (s->s_status == SERVICE_ZOMBIE)
@ -194,10 +197,12 @@ service_mapper_process ( service_t *s )
/* Find existing channel */
num = s->s_channel_number(s);
#if 0
if (service_mapper_conf.merge_same_name)
chn = channel_find_by_name(s->s_channel_name(s), 1, num);
else
chn = channel_create(s->s_channel_name(s));
chn = channel_find_by_name(s->s_channel_name(s));
#endif
if (!chn)
chn = channel_create(NULL, NULL, s->s_channel_name(s));
/* Map */
if (chn) {

View file

@ -42,7 +42,7 @@ void service_mapper_remove ( struct service *t );
int service_mapper_qlen ( void );
// Link service to channel
void service_mapper_link ( struct service *s, struct channel *c );
int service_mapper_link ( struct service *s, struct channel *c );
// Unlink service from channel
void service_mapper_unlink ( struct service *s, struct channel *c );

View file

@ -147,9 +147,6 @@ void gtimer_disarm(gtimer_t *gti);
* List / Queue header declarations
*/
LIST_HEAD(th_subscription_list, th_subscription);
RB_HEAD(channel_tree, channel);
TAILQ_HEAD(channel_queue, channel);
LIST_HEAD(channel_list, channel);
LIST_HEAD(dvr_config_list, dvr_config);
LIST_HEAD(dvr_entry_list, dvr_entry);
TAILQ_HEAD(ref_update_queue, ref_update);
@ -481,7 +478,6 @@ int rate_to_sri(int rate);
extern time_t dispatch_clock;
extern struct service_list all_transports;
extern struct channel_tree channel_name_tree;
extern void scopedunlock(pthread_mutex_t **mtxp);

View file

@ -37,7 +37,6 @@
#include "channels.h"
#include "dvr/dvr.h"
#include "service_mapper.h"
#include "epggrab.h"
#include "epg.h"
#include "muxer.h"
@ -318,221 +317,6 @@ extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
return 0;
}
/**
*
*/
static void
extjs_channels_delete(htsmsg_t *in)
{
htsmsg_field_t *f;
channel_t *ch;
TAILQ_FOREACH(f, &in->hm_fields, hmf_link)
if(f->hmf_type == HMF_S64 &&
(ch = channel_find_by_identifier(f->hmf_s64)) != NULL)
channel_delete(ch);
}
/**
*
*/
static void
extjs_channels_update(htsmsg_t *in)
{
htsmsg_field_t *f;
channel_t *ch;
htsmsg_t *c;
uint32_t id;
const char *s;
TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
if((c = htsmsg_get_map_by_field(f)) == NULL ||
htsmsg_get_u32(c, "id", &id))
continue;
if((ch = channel_find_by_identifier(id)) == NULL)
continue;
if((s = htsmsg_get_str(c, "name")) != NULL)
channel_rename(ch, s);
if((s = htsmsg_get_str(c, "ch_icon")) != NULL)
channel_set_icon(ch, s);
if((s = htsmsg_get_str(c, "tags")) != NULL)
channel_set_tags_from_list(ch, s);
if((s = htsmsg_get_str(c, "epg_pre_start")) != NULL)
channel_set_epg_postpre_time(ch, 1, atoi(s));
if((s = htsmsg_get_str(c, "epg_post_end")) != NULL)
channel_set_epg_postpre_time(ch, 0, atoi(s));
if((s = htsmsg_get_str(c, "number")) != NULL)
channel_set_number(ch, atoi(s));
if((s = htsmsg_get_str(c, "epggrabsrc")) != NULL) {
char *tmp = strdup(s);
char *sptr = NULL, *sptr2 = NULL;
char *modecid = strtok_r(tmp, ",", &sptr);
char *modid, *ecid;
epggrab_module_t *mod;
epggrab_channel_t *ec;
epggrab_channel_link_t *ecl;
/* Clear existing */
LIST_FOREACH(mod, &epggrab_modules, link) {
if (mod->type != EPGGRAB_OTA && mod->channels) {
RB_FOREACH(ec, mod->channels, link) {
LIST_FOREACH(ecl, &ec->channels, link) {
if (ecl->channel == ch) {
LIST_REMOVE(ecl, link);
free(ecl);
mod->ch_save(mod, ec);
break;
}
}
}
}
}
/* Add new */
while (modecid) {
modid = strtok_r(modecid, "|", &sptr2);
ecid = strtok_r(NULL, "|", &sptr2);
modecid = strtok_r(NULL, ",", &sptr);
if (!(mod = epggrab_module_find_by_id(modid)))
continue;
if (!mod->channels)
continue;
if (!(ec = epggrab_channel_find(mod->channels, ecid, 0, NULL, mod)))
continue;
epggrab_channel_link(ec, ch);
}
/* Cleanup */
free(tmp);
}
}
}
/**
*
*/
static htsmsg_t *
build_record_channel ( channel_t *ch )
{
char buf[1024];
channel_tag_mapping_t *ctm;
htsmsg_t *c;
char *epggrabsrc;
epggrab_module_t *mod;
epggrab_channel_t *ec;
epggrab_channel_link_t *ecl;
c = htsmsg_create_map();
htsmsg_add_str(c, "name", ch->ch_name);
htsmsg_add_u32(c, "chid", ch->ch_id);
if(ch->ch_icon != NULL) {
htsmsg_add_imageurl(c, "chicon", "imagecache/%d", ch->ch_icon);
htsmsg_add_str(c, "ch_icon", ch->ch_icon);
}
buf[0] = 0;
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
"%s%d", strlen(buf) == 0 ? "" : ",",
ctm->ctm_tag->ct_identifier);
}
htsmsg_add_str(c, "tags", buf);
htsmsg_add_s32(c, "epg_pre_start", ch->ch_dvr_extra_time_pre);
htsmsg_add_s32(c, "epg_post_end", ch->ch_dvr_extra_time_post);
htsmsg_add_s32(c, "number", ch->ch_number);
epggrabsrc = NULL;
LIST_FOREACH(mod, &epggrab_modules, link) {
if (mod->type != EPGGRAB_OTA && mod->channels) {
RB_FOREACH(ec, mod->channels, link) {
LIST_FOREACH(ecl, &ec->channels, link) {
if (ecl->channel == ch) {
char id[100];
sprintf(id, "%s|%s", mod->id, ec->id);
if (!epggrabsrc) {
epggrabsrc = strdup(id);
} else {
epggrabsrc = realloc(epggrabsrc, strlen(epggrabsrc) + 2 + strlen(id));
strcat(epggrabsrc, ",");
strcat(epggrabsrc, id);
}
}
}
}
}
}
if (epggrabsrc) htsmsg_add_str(c, "epggrabsrc", epggrabsrc);
free(epggrabsrc);
return c;
}
/**
*
*/
static int
extjs_channels(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hc->hc_reply;
htsmsg_t *array;
channel_t *ch;
const char *op = http_arg_get(&hc->hc_req_args, "op");
const char *entries = http_arg_get(&hc->hc_req_args, "entries");
if(op == NULL)
return 400;
htsmsg_t *in =
entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
htsmsg_t *out = htsmsg_create_map();
scopedgloballock();
if(!strcmp(op, "list")) {
array = htsmsg_create_list();
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
htsmsg_add_msg(array, NULL, build_record_channel(ch));
}
htsmsg_add_msg(out, "entries", array);
} else if(!strcmp(op, "create")) {
htsmsg_destroy(out);
out = build_record_channel(channel_create(NULL));
} else if(!strcmp(op, "delete") && in != NULL) {
extjs_channels_delete(in);
} else if(!strcmp(op, "update") && in != NULL) {
extjs_channels_update(in);
} else {
htsmsg_destroy(in);
htsmsg_destroy(out);
return 400;
}
htsmsg_json_serialize(out, hq, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
htsmsg_destroy(in);
htsmsg_destroy(out);
return 0;
}
/**
* EPG Content Groups
*/
@ -924,7 +708,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
m = htsmsg_create_map();
htsmsg_add_str(m, "channel", ch->ch_name);
htsmsg_add_u32(m, "channelid", ch->ch_id);
htsmsg_add_u32(m, "channelid", channel_get_id(ch));
if(ch->ch_icon != NULL)
htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
@ -1186,7 +970,7 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
const char *channel = http_arg_get(&hc->hc_req_args, "channelid");
const char *pri = http_arg_get(&hc->hc_req_args, "pri");
channel_t *ch = channel ? channel_find_by_identifier(atoi(channel)) : NULL;
channel_t *ch = channel ? channel_find_by_id(atoi(channel)) : NULL;
if(ch == NULL || title == NULL ||
datestr == NULL || strlen(datestr) != 10 ||
@ -1653,49 +1437,6 @@ extjs_servicedetails(http_connection_t *hc,
return 0;
}
/**
*
*/
static int
extjs_mergechannel(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hc->hc_reply;
const char *target = http_arg_get(&hc->hc_req_args, "targetID");
htsmsg_t *out;
channel_t *src, *dst;
if(remain == NULL || target == NULL)
return 400;
pthread_mutex_lock(&global_lock);
src = channel_find_by_identifier(atoi(remain));
dst = channel_find_by_identifier(atoi(target));
if(src == NULL || dst == NULL) {
pthread_mutex_unlock(&global_lock);
return 404;
}
out = htsmsg_create_map();
if(src != dst) {
channel_merge(dst, src);
htsmsg_add_u32(out, "success", 1);
} else {
htsmsg_add_u32(out, "success", 0);
htsmsg_add_str(out, "msg", "Target same as source");
}
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;
}
/**
*
*/
@ -1972,67 +1713,6 @@ extjs_timeshift(http_connection_t *hc, const char *remain, void *opaque)
}
#endif
static int
extjs_service_mapping
(http_connection_t *hc, const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hc->hc_reply;
htsmsg_t *out;
service_mapper_conf_t conf = { 0 };
const char *op = http_arg_get(&hc->hc_req_args, "op");
if (!op)
return HTTP_STATUS_BAD_REQUEST;
/* Status */
if (!strcmp(op, "status")) {
int num;
pthread_mutex_lock(&global_lock);
num = service_mapper_qlen();
pthread_mutex_unlock(&global_lock);
out = htsmsg_create_map();
htsmsg_add_u32(out, "remaining", num);
/* Start */
} else if (!strcmp(op, "start")) {
/* Get config */
if (http_arg_get(&hc->hc_req_args, "check_availability") != NULL)
conf.check_availability = 1;
if (http_arg_get(&hc->hc_req_args, "encrypted") != NULL)
conf.encrypted = 1;
if (http_arg_get(&hc->hc_req_args, "merge_same_name") != NULL)
conf.merge_same_name = 1;
if (http_arg_get(&hc->hc_req_args, "provider_tags") != NULL)
conf.provider_tags = 1;
/* Start */
pthread_mutex_lock(&global_lock);
service_mapper_start(&conf);
pthread_mutex_unlock(&global_lock);
out = htsmsg_create_map();
/* Stop */
} else if (!strcmp(op, "stop")) {
pthread_mutex_lock(&global_lock);
service_mapper_stop();
pthread_mutex_unlock(&global_lock);
out = htsmsg_create_map();
/* Invalid */
} else {
return HTTP_STATUS_BAD_REQUEST;
}
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output_content(hc, "text/x-json; charset=UTF-8");
return 0;
}
/**
* WEB user interface
*/
@ -2043,7 +1723,6 @@ extjs_start(void)
http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE);
http_path_add("/capabilities", NULL, extjs_capabilities, ACCESS_WEB_INTERFACE);
http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE);
http_path_add("/channels", NULL, extjs_channels, 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);
@ -2059,15 +1738,12 @@ extjs_start(void)
http_path_add("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE);
http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE);
http_path_add("/languages", NULL, extjs_languages, ACCESS_WEB_INTERFACE);
http_path_add("/mergechannel", NULL, extjs_mergechannel, ACCESS_ADMIN);
http_path_add("/servicedetails", NULL, extjs_servicedetails, ACCESS_ADMIN);
#if ENABLE_TIMESHIFT
http_path_add("/timeshift", NULL, extjs_timeshift, ACCESS_ADMIN);
#endif
http_path_add("/tvhlog", NULL, extjs_tvhlog, ACCESS_ADMIN);
http_path_add("/api/service_mapping", NULL, extjs_service_mapping, ACCESS_ADMIN);
#if ENABLE_V4L
extjs_start_v4l();
#endif

View file

@ -55,7 +55,7 @@ dumpchannels(htsbuf_queue_t *hq)
channel_t *ch;
outputtitle(hq, 0, "Channels");
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
CHANNEL_FOREACH(ch) {
htsbuf_qprintf(hq, "%s (%d)\n", ch->ch_name, ch->ch_id);
htsbuf_qprintf(hq,

View file

@ -25,6 +25,7 @@ tvheadend.channelrec = new Ext.data.Record.create(
[ 'name', 'chid', 'epggrabsrc', 'tags', 'ch_icon', 'epg_pre_start',
'epg_post_end', 'number' ]);
/*
tvheadend.channels = new Ext.data.JsonStore({
autoLoad : true,
root : 'entries',
@ -43,6 +44,7 @@ tvheadend.channels = new Ext.data.JsonStore({
tvheadend.comet.on('channels', function(m) {
if (m.reload != null) tvheadend.channels.reload();
});
*/
/*
* Service mapping
@ -466,3 +468,24 @@ tvheadend.chconf = function() {
return grid;
}
tvheadend.channel_tab = function(panel)
{
var mapButton = new Ext.Toolbar.Button({
tooltip : 'Map services to channels',
iconCls : '',
text : 'Map Services',
handler : tvheadend.mapServices,
disabled : false,
});
tvheadend.idnode_grid(panel, {
url : 'api/channel',
comet : 'channel',
titleS : 'Channel',
titleP : 'Channels',
add : false,
del : false,
tbar : [ mapButton ]
});
}

View file

@ -111,7 +111,10 @@ tvheadend.idnode_editor_field = function(f, create)
switch(f.type) {
case 'str':
if (f.enum) {
return new Ext.form.ComboBox({
var cons = Ext.form.ComboBox;
if (f.multi)
cons = Ext.ux.form.LovCombo;
return new cons({
fieldLabel : f.caption,
name : f.id,
value : f.value,
@ -124,7 +127,17 @@ tvheadend.idnode_editor_field = function(f, create)
typeAhead : true,
forceSelection : true,
triggerAction : 'all',
emptyText :'Select ' + f.caption +' ...'
emptyText :'Select ' + f.caption +' ...',
listeners : {
keyup: function() {
this.store.filter('val', this.getRawValue(), true, false);
},
beforequery: function(queryEvent) {
queryEvent.combo.onLoad();
// prevent doQuery from firing and clearing out my filter.
return false;
}
}
});
} else {
return new Ext.form.TextField({
@ -615,14 +628,24 @@ tvheadend.idnode_grid = function(panel, conf)
}
});
buttons.push(editBtn);
buttons.push('->');
/* Extra buttons */
if (conf.tbar) {
buttons.push('-')
for (i = 0; i < conf.tbar.length; i++)
buttons.push(conf.tbar[i])
}
/* Help */
if (conf.help) {
buttons.push('->');
buttons.push({
text : 'Help',
handler : conf.help
});
}
/* Grid Panel */
var auto = new Ext.form.Checkbox({
checked : true,

View file

@ -290,11 +290,11 @@ function accessUpdate(o) {
title : 'Channel / EPG',
iconCls : 'television',
items : [
new tvheadend.chconf,
new tvheadend.cteditor,
new tvheadend.epggrab
]
});
tvheadend.channel_tab(tvheadend.conf_chepg);
tabs1.push(tvheadend.conf_chepg);
/* DVR / Timeshift */

View file

@ -290,7 +290,7 @@ http_channel_playlist(http_connection_t *hc, channel_t *channel)
hq = &hc->hc_reply;
host = http_arg_get(&hc->hc_args, "Host");
snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel->ch_id);
snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(channel));
htsbuf_qprintf(hq, "#EXTM3U\n");
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel->ch_name);
@ -319,7 +319,7 @@ http_tag_playlist(http_connection_t *hc, channel_tag_t *tag)
htsbuf_qprintf(hq, "#EXTM3U\n");
LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
snprintf(buf, sizeof(buf), "/stream/channelid/%d", ctm->ctm_channel->ch_id);
snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(ctm->ctm_channel));
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ctm->ctm_channel->ch_name);
htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf,
access_ticket_create(buf));
@ -377,8 +377,8 @@ http_channel_list_playlist(http_connection_t *hc)
host = http_arg_get(&hc->hc_args, "Host");
htsbuf_qprintf(hq, "#EXTM3U\n");
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
snprintf(buf, sizeof(buf), "/stream/channelid/%d", ch->ch_id);
CHANNEL_FOREACH(ch) {
snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(ch));
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", ch->ch_name);
htsbuf_qprintf(hq, "http://%s%s?ticket=%s\n", host, buf,
@ -511,9 +511,9 @@ page_http_playlist(http_connection_t *hc, const char *remain, void *opaque)
pthread_mutex_lock(&global_lock);
if(nc == 2 && !strcmp(components[0], "channelid"))
ch = channel_find_by_identifier(atoi(components[1]));
ch = channel_find_by_id(atoi(components[1]));
else if(nc == 2 && !strcmp(components[0], "channel"))
ch = channel_find_by_name(components[1], 0, 0);
ch = channel_find_by_name(components[1]);
else if(nc == 2 && !strcmp(components[0], "dvrid"))
de = dvr_entry_find_by_id(atoi(components[1]));
else if(nc == 2 && !strcmp(components[0], "tagid"))
@ -748,9 +748,9 @@ http_stream(http_connection_t *hc, const char *remain, void *opaque)
scopedgloballock();
if(!strcmp(components[0], "channelid")) {
ch = channel_find_by_identifier(atoi(components[1]));
ch = channel_find_by_id(atoi(components[1]));
} else if(!strcmp(components[0], "channel")) {
ch = channel_find_by_name(components[1], 0, 0);
ch = channel_find_by_name(components[1]);
} else if(!strcmp(components[0], "service")) {
service = service_find_by_identifier(components[1]);
#if 0//ENABLE_LINUXDVB