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 += \ SRCS += \
src/api.c \ src/api.c \
src/api/api_idnode.c \ src/api/api_idnode.c \
src/api/api_channel.c \
src/api/api_service.c \ src/api/api_service.c \
src/api/api_mpegts.c \ src/api/api_mpegts.c \

View file

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

View file

@ -59,6 +59,7 @@ void api_init ( void );
void api_idnode_init ( void ); void api_idnode_init ( void );
void api_mpegts_init ( void ); void api_mpegts_init ( void );
void api_service_init ( void ); void api_service_init ( void );
void api_channel_init ( void );
/* /*
* IDnode * IDnode
@ -85,4 +86,7 @@ int api_idnode_class
int api_idnode_tree int api_idnode_tree
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ); ( 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__ */ #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; return 0;
} }
static int int
api_idnode_load_by_class 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; int i, _enum;
const idclass_t *idc; const idclass_t *idc;
@ -149,10 +149,8 @@ api_idnode_load_by_class
pthread_mutex_lock(&global_lock); pthread_mutex_lock(&global_lock);
/* Find class */ /* Find class */
if (!(idc = idclass_find(class))) { idc = opaque;
pthread_mutex_unlock(&global_lock); assert(idc);
return EINVAL;
}
l = htsmsg_create_list(); l = htsmsg_create_list();
if ((is = idnode_find_all(idc))) { if ((is = idnode_find_all(idc))) {
@ -162,7 +160,7 @@ api_idnode_load_by_class
/* Name/UUID only */ /* Name/UUID only */
if (_enum) { if (_enum) {
e = htsmsg_create_map(); 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)); htsmsg_add_str(e, "val", idnode_get_title(in));
/* Full record */ /* Full record */
@ -192,8 +190,16 @@ api_idnode_load
const char *uuid, *class; const char *uuid, *class;
/* Class based */ /* Class based */
if ((class = htsmsg_get_str(args, "class"))) if ((class = htsmsg_get_str(args, "class"))) {
return api_idnode_load_by_class(class, args, resp); 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 */ /* UUIDs */
if (!(f = htsmsg_field_find(args, "uuid"))) if (!(f = htsmsg_field_find(args, "uuid")))

View file

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

View file

@ -40,12 +40,14 @@
#include "htsp_server.h" #include "htsp_server.h"
#include "imagecache.h" #include "imagecache.h"
#include "service_mapper.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; struct channel_tag_queue channel_tags;
static dtable_t *channeltags_dtable; static dtable_t *channeltags_dtable;
static void channel_tag_init ( void );
static channel_tag_t *channel_tag_find(const char *id, int create); static channel_tag_t *channel_tag_find(const char *id, int create);
static void channel_tag_mapping_destroy(channel_tag_mapping_t *ctm, static void channel_tag_mapping_destroy(channel_tag_mapping_t *ctm,
int flags); 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_TAG 0x1
#define CTM_DESTROY_UPDATE_CHANNEL 0x2 #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 static void
channel_list_changed(void) channel_class_save ( idnode_t *self )
{ {
htsmsg_t *m = htsmsg_create_map(); channel_save((channel_t*)self);
htsmsg_add_u32(m, "reload", 1);
notify_by_msg("channels", m);
} }
static const void *
static int channel_class_services_get ( void *obj )
dictcmp(const char *a, const char *b)
{ {
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) { /* Add all */
switch((*a >= '0' && *a <= '9' ? 1 : 0)|(*b >= '0' && *b <= '9' ? 2 : 0)) { LIST_FOREACH(csm, &ch->ch_services, csm_svc_link) {
case 0: /* 0: a is not a digit, nor is b */ if (first)
if(*a != *b) htsbuf_queue_init(&hq, 0);
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;
else else
*cp++ = '_'; htsbuf_append(&hq, ",", 1);
} uuid = idnode_uuid_as_str(&csm->csm_svc->s_id);
*cp = 0; htsbuf_append(&hq, uuid, strlen(uuid));
first = 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;
} }
if (!name || !*name) { /* Build string */
snprintf(buf, sizeof(buf), "Channel %d", id); if (!first) {
name = buf; s = htsbuf_to_string(&hq);
htsbuf_queue_flush(&hq);
} }
ch = calloc(1, sizeof(channel_t)); return &s;
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;
} }
/** static int
* channel_class_services_set ( void *obj, const void *p )
*/
channel_t *
channel_create ( const char *name )
{ {
channel_t *ch = channel_create2(name, 0); return channel_set_services_by_list(obj, p);
channel_save(ch);
return ch;
} }
/** static htsmsg_t *
* channel_class_services_enum ( void *obj )
*/
channel_t *
channel_find_by_name(const char *name, int create, int channel_number)
{ {
lock_assert(&global_lock); htsmsg_t *e, *m = htsmsg_create_map();
channel_t *ch, skel; 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) { static const void *
skel.ch_name = (char *)name; channel_class_tags_get ( void *obj )
ch = RB_FIND(&channel_name_tree, &skel, ch_name_link, channelcmp); {
if(ch != NULL || create == 0) static char *s = NULL;
return ch; 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;
} }
static int
/** channel_class_tags_set ( void *obj, const void *p )
*
*/
channel_t *
channel_find_by_identifier(int id)
{ {
channel_t skel, *ch; return channel_set_tags_by_list(obj, p);
}
lock_assert(&global_lock);
static htsmsg_t *
skel.ch_id = id; channel_class_tags_enum ( void *obj )
ch = RB_FIND(&channel_identifier_tree, &skel, ch_identifier_link, chidcmp); {
return ch; 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 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; channel_t *ch;
const char *name = htsmsg_get_str(c, "name"); CHANNEL_FOREACH(ch)
htsmsg_t *tags; if (!strcmp(ch->ch_name ?: "", name))
htsmsg_field_t *f; break;
channel_tag_t *ct; return ch;
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_t *
/** channel_find_by_id ( uint32_t i )
*
*/
static void
channels_load(void)
{ {
htsmsg_t *l, *c; channel_t skel;
htsmsg_field_t *f; memcpy(skel.ch_id.in_uuid, &i, sizeof(i));
if((l = hts_settings_load("channels")) != NULL) { return RB_FIND(&channels, &skel, ch_link, ch_id_cmp);
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);
}
} }
/* **************************************************************************
* 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 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)) /* Remove */
return -1; for (csm = LIST_FIRST(&ch->ch_services); csm != NULL; csm = n) {
n = LIST_NEXT(csm, csm_chn_link);
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" renamed to \"%s\"", if (csm->csm_mark) {
ch->ch_name, newname); LIST_REMOVE(csm, csm_chn_link);
LIST_REMOVE(csm, csm_svc_link);
RB_REMOVE(&channel_name_tree, ch, ch_name_link); free(csm);
channel_set_name(ch, newname); save = 1;
epggrab_channel_mod(ch); }
LIST_FOREACH(de, &ch->ch_dvrs, de_channel_link) {
dvr_entry_save(de);
dvr_entry_notify(de);
} }
channel_save(ch); return save;
htsp_channel_update(ch); }
int
channel_set_tags_by_list ( channel_t *ch, const char *tags )
{
return 0; return 0;
} }
/** /* **************************************************************************
* Delete channel * Creation/Deletion
*/ * *************************************************************************/
void
channel_delete(channel_t *ch) 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; th_subscription_t *s;
channel_tag_mapping_t *ctm; channel_tag_mapping_t *ctm;
channel_service_mapping_t *csm;
lock_assert(&global_lock); lock_assert(&global_lock);
tvhinfo("channel", "%s - deleting", ch->ch_name);
/* Tags */
while((ctm = LIST_FIRST(&ch->ch_ctms)) != NULL) while((ctm = LIST_FIRST(&ch->ch_ctms)) != NULL)
channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_TAG); channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_TAG);
tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" deleted", /* DVR */
ch->ch_name);
autorec_destroy_by_channel(ch); autorec_destroy_by_channel(ch);
dvr_destroy_by_channel(ch); dvr_destroy_by_channel(ch);
while((t = LIST_FIRST(&ch->ch_services)) != NULL) /* Services */
service_mapper_unlink(t->csm_svc, ch); while((csm = LIST_FIRST(&ch->ch_services)) != NULL)
service_mapper_unlink(csm->csm_svc, ch);
/* Subscriptions */
while((s = LIST_FIRST(&ch->ch_subscriptions)) != NULL) { while((s = LIST_FIRST(&ch->ch_subscriptions)) != NULL) {
LIST_REMOVE(s, ths_channel_link); LIST_REMOVE(s, ths_channel_link);
s->ths_channel = NULL; s->ths_channel = NULL;
} }
/* EPG */
#if 0
epggrab_channel_rem(ch); epggrab_channel_rem(ch);
epg_channel_unlink(ch); epg_channel_unlink(ch);
#endif
hts_settings_remove("channels/%d", ch->ch_id); /* Settings */
hts_settings_remove("channel/%s", idnode_uuid_as_str(&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);
/* Free memory */
RB_REMOVE(&channels, ch, ch_link);
idnode_unlink(&ch->ch_id);
free(ch->ch_name); free(ch->ch_name);
free(ch->ch_sname);
free(ch->ch_icon); free(ch->ch_icon);
channel_list_changed();
free(ch); free(ch);
} }
/*
* Save
/**
* Merge services from channel 'src' to channel 'dst'
*
* Then, destroy the 'src' channel
*/ */
void 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\"", /* Tags */
src->ch_name, dst->ch_name); channel_tag_init();
while((t = LIST_FIRST(&src->ch_services)) != NULL) /* Channels */
service_mapper_link(t->csm_svc, dst); if (!(c = hts_settings_load_r(1, "channel")))
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))
return; return;
free(ch->ch_icon); HTSMSG_FOREACH(f, c) {
ch->ch_icon = strdup(icon); if (!(e = htsmsg_field_get_map(f))) continue;
imagecache_get_id(icon); (void)channel_create(f->hmf_name, e, NULL);
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;
} }
channel_save(ch); htsmsg_destroy(c);
htsp_channel_update(ch);
} }
/** /* ***
* 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; return NULL;
} }
static void
/** channel_tag_init ( void )
*
*/
void
channels_init(void)
{ {
TAILQ_INIT(&channel_tags); TAILQ_INIT(&channel_tags);
channeltags_dtable = dtable_create(&channel_tags_dtc, "channeltags", NULL); channeltags_dtable = dtable_create(&channel_tags_dtc, "channeltags", NULL);
dtable_load(channeltags_dtable); dtable_load(channeltags_dtable);
channels_load();
} }

View file

@ -20,51 +20,54 @@
#define CHANNELS_H #define CHANNELS_H
#include "epg.h" #include "epg.h"
#include "idnode.h"
RB_HEAD(channel_tree, channel);
LIST_HEAD(channel_tag_mapping_list, channel_tag_mapping); LIST_HEAD(channel_tag_mapping_list, channel_tag_mapping);
TAILQ_HEAD(channel_tag_queue, channel_tag); TAILQ_HEAD(channel_tag_queue, channel_tag);
extern struct channel_tag_queue channel_tags; extern struct channel_tag_queue channel_tags;
extern struct channel_tree channels;
#define CHANNEL_FOREACH(ch) RB_FOREACH(ch, &channels, ch_link)
/* /*
* Channel definition * Channel definition
*/ */
typedef struct channel { typedef struct channel
{
idnode_t ch_id;
RB_ENTRY(channel) ch_link;
int ch_refcount; int ch_refcount;
int ch_zombie; int ch_zombie;
RB_ENTRY(channel) ch_name_link; /* Channel info */
char *ch_name; char *ch_name;
char *ch_sname; int ch_number;
char *ch_icon;
RB_ENTRY(channel) ch_identifier_link; struct channel_tag_mapping_list ch_ctms;
int ch_id;
/* Service/subscriptions */
LIST_HEAD(, channel_service_mapping) ch_services; LIST_HEAD(, channel_service_mapping) ch_services;
LIST_HEAD(, th_subscription) ch_subscriptions; LIST_HEAD(, th_subscription) ch_subscriptions;
/* EPG fields */ /* EPG fields */
epg_broadcast_tree_t ch_epg_schedule; epg_broadcast_tree_t ch_epg_schedule;
epg_broadcast_t *ch_epg_now; epg_broadcast_t *ch_epg_now;
epg_broadcast_t *ch_epg_next; epg_broadcast_t *ch_epg_next;
gtimer_t ch_epg_timer; gtimer_t ch_epg_timer;
gtimer_t ch_epg_timer_head;
gtimer_t ch_epg_timer_current;
gtimer_t ch_epg_timer_head; /* DVR */
gtimer_t ch_epg_timer_current; int ch_dvr_extra_time_pre;
int ch_dvr_extra_time_pre; int ch_dvr_extra_time_post;
int ch_dvr_extra_time_post;
int ch_number; // User configurable number
char *ch_icon;
struct dvr_entry_list ch_dvrs; struct dvr_entry_list ch_dvrs;
struct dvr_autorec_entry_list ch_autorecs; struct dvr_autorec_entry_list ch_autorecs;
struct channel_tag_mapping_list ch_ctms;
} channel_t; } channel_t;
@ -85,7 +88,6 @@ typedef struct channel_tag {
struct dvr_autorec_entry_list ct_autorecs; struct dvr_autorec_entry_list ct_autorecs;
} channel_tag_t; } channel_tag_t;
/** /**
* Channel tag mapping * Channel tag mapping
*/ */
@ -100,39 +102,41 @@ typedef struct channel_tag_mapping {
} channel_tag_mapping_t; } channel_tag_mapping_t;
/*
* Service mappings
*/
typedef struct channel_service_mapping { typedef struct channel_service_mapping {
LIST_ENTRY(channel_service_mapping) csm_chn_link; LIST_ENTRY(channel_service_mapping) csm_chn_link;
LIST_ENTRY(channel_service_mapping) csm_svc_link; LIST_ENTRY(channel_service_mapping) csm_svc_link;
struct channel *csm_chn; struct channel *csm_chn;
struct service *csm_svc; struct service *csm_svc;
int csm_mark;
} channel_service_mapping_t; } 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_create0
(channel_t *ch, const idclass_t *idc, const char *uuid, htsmsg_t *conf,
channel_t *channel_find_by_identifier(int id); const char *name);
#define channel_create(u, c, n)\
void channel_set_teletext_rundown(channel_t *ch, int v); channel_create0(calloc(1, sizeof(channel_t)), &channel_class, u, c, n)
void channel_settings_write(channel_t *ch);
int channel_rename(channel_t *ch, const char *newname);
void channel_delete(channel_t *ch); 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); int channel_set_tags_by_list ( channel_t *ch, const char *tags );
int channel_set_services_by_list ( channel_t *ch, const char *svcs );
void channel_set_tags_from_list(channel_t *ch, const char *maplist);
channel_tag_t *channel_tag_find_by_name(const char *name, int create); 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); 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 */ #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); LIST_REMOVE(dae, dae_channel_link);
dae->dae_channel = NULL; 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); LIST_INSERT_HEAD(&ch->ch_autorecs, dae, dae_channel_link);
dae->dae_channel = ch; dae->dae_channel = ch;
} }
@ -553,7 +553,7 @@ dvr_autorec_add(const char *config_name,
const char *creator, const char *comment) const char *creator, const char *comment)
{ {
channel_t *ch = NULL; 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, _dvr_autorec_add(config_name, title, ch, tag, content_type,
NULL, NULL, NULL, 0, NULL, creator, comment); NULL, NULL, NULL, 0, NULL, creator, comment);
} }
@ -624,7 +624,7 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge)
if (purge) if (purge)
dvr_autorec_purge_spawns(dae); 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) { RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
if(autorec_cmp(dae, e)) if(autorec_cmp(dae, e))
dvr_entry_create_by_autorec(e, dae); 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_db_load_one(htsmsg_t *c, int id)
{ {
dvr_entry_t *de; dvr_entry_t *de;
const char *chname, *s, *creator; const char *chuuid, *chname, *s, *creator;
channel_t *ch; channel_t *ch;
uint32_t start, stop, bcid, u32; uint32_t start, stop, bcid, u32;
int d; int d;
@ -531,9 +531,15 @@ dvr_db_load_one(htsmsg_t *c, int id)
if(htsmsg_get_u32(c, "stop", &stop)) if(htsmsg_get_u32(c, "stop", &stop))
return; return;
if((chname = htsmsg_get_str(c, "channel")) == NULL) chname = htsmsg_get_str(c, "channel_name");
return; chuuid = htsmsg_get_str(c, "channel");
ch = channel_find_by_name(chname, 0, 0); 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"); s = htsmsg_get_str(c, "config_name");
cfg = dvr_config_find_by_name_default(s); cfg = dvr_config_find_by_name_default(s);
@ -648,6 +654,8 @@ dvr_entry_save(dvr_entry_t *de)
lock_assert(&global_lock); 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_str(m, "channel", DVR_CH_NAME(de));
htsmsg_add_u32(m, "start", de->de_start); htsmsg_add_u32(m, "start", de->de_start);
htsmsg_add_u32(m, "stop", de->de_stop); 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) return NULL;
if (ebc->episode) return ebc->episode; if (ebc->episode) return ebc->episode;
if (!create) return NULL; if (!create) return NULL;
snprintf(uri, sizeof(uri)-1, "tvh://channel-%d/bcast-%u/episode", snprintf(uri, sizeof(uri)-1, "tvh://channel-%s/bcast-%u/episode",
ebc->channel->ch_id, ebc->id); idnode_uuid_as_str(&ebc->channel->ch_id), ebc->id);
if ((ee = epg_episode_find_by_uri(uri, 1, save))) if ((ee = epg_episode_find_by_uri(uri, 1, save)))
*save |= epg_broadcast_set_episode(ebc, ee, ebc->grabber); *save |= epg_broadcast_set_episode(ebc, ee, ebc->grabber);
return ee; return ee;
@ -1756,7 +1756,7 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
htsmsg_add_s64(m, "stop", broadcast->stop); htsmsg_add_s64(m, "stop", broadcast->stop);
htsmsg_add_str(m, "episode", broadcast->episode->uri); htsmsg_add_str(m, "episode", broadcast->episode->uri);
if (broadcast->channel) 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) if (broadcast->dvb_eid)
htsmsg_add_u32(m, "dvb_eid", broadcast->dvb_eid); htsmsg_add_u32(m, "dvb_eid", broadcast->dvb_eid);
if (broadcast->is_widescreen) if (broadcast->is_widescreen)
@ -1796,7 +1796,7 @@ epg_broadcast_t *epg_broadcast_deserialize
epg_serieslink_t *esl; epg_serieslink_t *esl;
lang_str_t *ls; lang_str_t *ls;
const char *str; const char *str;
uint32_t chid, eid, u32; uint32_t eid, u32;
int64_t start, stop; int64_t start, stop;
if ( htsmsg_get_s64(m, "start", &start) ) return NULL; if ( htsmsg_get_s64(m, "start", &start) ) return NULL;
@ -1818,9 +1818,8 @@ epg_broadcast_t *epg_broadcast_deserialize
} }
/* Get channel */ /* Get channel */
if ( !htsmsg_get_u32(m, "channel", &chid) ) { if ((str = htsmsg_get_str(m, "channel")))
ch = channel_find_by_identifier(chid); ch = channel_find(str);
}
if (!ch) return NULL; if (!ch) return NULL;
/* Create */ /* Create */
@ -2254,9 +2253,11 @@ void epg_query0
/* All channels */ /* All channels */
} else { } else {
#if TODO
RB_FOREACH(channel, &channel_name_tree, ch_name_link) { RB_FOREACH(channel, &channel_name_tree, ch_name_link) {
_eqr_add_channel(eqr, channel, genre, preg, now, lang); _eqr_add_channel(eqr, channel, genre, preg, now, lang);
} }
#endif
} }
if (preg) regfree(preg); 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, void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
epg_genre_t *genre, const char *title, const char *lang) epg_genre_t *genre, const char *title, const char *lang)
{ {
channel_t *ch = channel ? channel_find_by_name(channel, 0, 0) : NULL; 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_name(tag, 0) : NULL;
epg_query0(eqr, ch, ct, genre, title, lang); 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 * Use for v1 databases
*/ */
#if DEPRECATED
static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats ) static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats )
{ {
channel_t *ch; channel_t *ch;
@ -94,6 +95,7 @@ static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats )
/* Set episode */ /* Set episode */
save |= epg_broadcast_set_episode(ebc, ee, NULL); save |= epg_broadcast_set_episode(ebc, ee, NULL);
} }
#endif
/* /*
* Process v2 data * Process v2 data
@ -207,8 +209,7 @@ void epg_init ( void )
case 2: case 2:
_epgdb_v2_process(m, &stats); _epgdb_v2_process(m, &stats);
break; break;
default: /* v0/1 */ default:
_epgdb_v1_process(m, &stats);
break; break;
} }
@ -267,8 +268,10 @@ void epg_save ( void *p )
{ {
int fd; int fd;
epg_object_t *eo; epg_object_t *eo;
#if 0
epg_broadcast_t *ebc; epg_broadcast_t *ebc;
channel_t *ch; channel_t *ch;
#endif
epggrab_stats_t stats; epggrab_stats_t stats;
extern gtimer_t epggrab_save_timer; extern gtimer_t epggrab_save_timer;
@ -300,12 +303,14 @@ void epg_save ( void *p )
stats.seasons.total++; stats.seasons.total++;
} }
if ( _epg_write_sect(fd, "broadcasts") ) return; if ( _epg_write_sect(fd, "broadcasts") ) return;
#if 0
RB_FOREACH(ch, &channel_name_tree, ch_name_link) { RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) { RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
if (_epg_write(fd, epg_broadcast_serialize(ebc))) return; if (_epg_write(fd, epg_broadcast_serialize(ebc))) return;
stats.broadcasts.total++; stats.broadcasts.total++;
} }
} }
#endif
/* Stats */ /* Stats */
tvhlog(LOG_INFO, "epgdb", "saved"); 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 = calloc(1, sizeof(epggrab_channel_link_t));
ecl->channel = ch; ecl->channel = ch;
LIST_INSERT_HEAD(&ec->channels, ecl, link); LIST_INSERT_HEAD(&ec->channels, ecl, link);
#if 0
if (ec->name && epggrab_channel_rename) if (ec->name && epggrab_channel_rename)
channel_rename(ch, ec->name); channel_rename(ch, ec->name);
if (ec->icon && epggrab_channel_reicon) if (ec->icon && epggrab_channel_reicon)
channel_set_icon(ch, ec->icon); channel_set_icon(ch, ec->icon);
if (ec->number>0 && epggrab_channel_renumber) if (ec->number>0 && epggrab_channel_renumber)
channel_set_number(ch, ec->number); channel_set_number(ch, ec->number);
#endif
/* Save */ /* Save */
if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec); 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 epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
{ {
int save = 0; int save = 0;
#if 0
epggrab_channel_link_t *ecl; epggrab_channel_link_t *ecl;
if (!ec || !name) return 0; if (!ec || !name) return 0;
if (!ec->name || strcmp(ec->name, name)) { 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); channel_rename(ecl->channel, name);
save = 1; save = 1;
} }
#endif
return save; 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 epggrab_channel_set_icon ( epggrab_channel_t *ec, const char *icon )
{ {
int save = 0; int save = 0;
#if 0
epggrab_channel_link_t *ecl; epggrab_channel_link_t *ecl;
if (!ec->icon || strcmp(ec->icon, icon) ) { if (!ec->icon || strcmp(ec->icon, icon) ) {
if (!ec | !icon) return 0; 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); channel_set_icon(ecl->channel, icon);
save = 1; save = 1;
} }
#endif
return save; 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 epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
{ {
int save = 0; int save = 0;
#if 0
epggrab_channel_link_t *ecl; epggrab_channel_link_t *ecl;
if (!ec || (number <= 0)) return 0; if (!ec || (number <= 0)) return 0;
if (ec->number != number) { 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); channel_set_number(ecl->channel, number);
save = 1; save = 1;
} }
#endif
return save; return save;
} }
/* Channel settings updated */ /* Channel settings updated */
void epggrab_channel_updated ( epggrab_channel_t *ec ) void epggrab_channel_updated ( epggrab_channel_t *ec )
{ {
#if 0
channel_t *ch; channel_t *ch;
if (!ec) return; if (!ec) return;
@ -141,6 +150,7 @@ void epggrab_channel_updated ( epggrab_channel_t *ec )
/* Save */ /* Save */
if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec); if (ec->mod->ch_save) ec->mod->ch_save(ec->mod, ec);
#endif
} }
/* ID comparison */ /* 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); htsmsg_add_str(m, "icon", ch->icon);
LIST_FOREACH(ecl, &ch->channels, link) { LIST_FOREACH(ecl, &ch->channels, link) {
if (!a) a = htsmsg_create_list(); 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 (a) htsmsg_add_msg(m, "channels", a);
if (ch->number) if (ch->number)
@ -214,7 +214,7 @@ static void _epggrab_module_channel_load
egc->number = u32; egc->number = u32;
if ((a = htsmsg_get_list(m, "channels"))) { if ((a = htsmsg_get_list(m, "channels"))) {
HTSMSG_FOREACH(f, a) { 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)); epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t));
ecl->channel = ch; ecl->channel = ch;
LIST_INSERT_HEAD(&egc->channels, ecl, link); LIST_INSERT_HEAD(&egc->channels, ecl, link);
@ -223,7 +223,7 @@ static void _epggrab_module_channel_load
/* Compat with older 3.1 code */ /* Compat with older 3.1 code */
} else if (!htsmsg_get_u32(m, "channel", &u32)) { } 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)); epggrab_channel_link_t *ecl = calloc(1, sizeof(epggrab_channel_link_t));
ecl->channel = ch; ecl->channel = ch;
LIST_INSERT_HEAD(&egc->channels, ecl, link); 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 *tags = htsmsg_create_list();
htsmsg_t *services = 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_u32(out, "channelNumber", ch->ch_number);
htsmsg_add_str(out, "channelName", ch->ch_name); 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) { if(members != NULL) {
LIST_FOREACH(ctm, &ct->ct_ctms, ctm_tag_link) 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); 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); htsmsg_add_u32(out, "id", de->de_id);
if (de->de_channel) 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, "start", de->de_start);
htsmsg_add_s64(out, "stop", de->de_stop); htsmsg_add_s64(out, "stop", de->de_stop);
@ -647,7 +647,7 @@ htsp_build_event
htsmsg_add_str(out, "method", method); htsmsg_add_str(out, "method", method);
htsmsg_add_u32(out, "eventId", e->id); 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, "start", e->start);
htsmsg_add_s64(out, "stop", e->stop); htsmsg_add_s64(out, "stop", e->stop);
if ((str = epg_broadcast_get_title(e, lang))) 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); htsp_send_message(htsp, htsp_build_tag(ct, "tagAdd", 0), NULL);
/* Send all channels */ /* 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); htsp_send_message(htsp, htsp_build_channel(ch, "channelAdd", htsp), NULL);
/* Send all enabled and external tags (now with channel mappings) */ /* 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 */ /* Send EPG updates */
if (epg) { if (epg) {
RB_FOREACH(ch, &channel_name_tree, ch_name_link) CHANNEL_FOREACH(ch)
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) { RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
if (epgMaxTime && ebc->start > epgMaxTime) break; if (epgMaxTime && ebc->start > epgMaxTime) break;
htsmsg_t *e = htsp_build_event(ebc, "eventAdd", lang, lastUpdate, htsp); 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 */ /* Optional fields */
if (!htsmsg_get_u32(in, "channelId", &u32)) 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"); return htsp_error("Channel does not exist");
if (!htsmsg_get_u32(in, "eventId", &u32)) if (!htsmsg_get_u32(in, "eventId", &u32))
if (!(e = epg_broadcast_find_by_id(u32, ch))) 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 */ /* All channels */
} else { } else {
events = htsmsg_create_list(); events = htsmsg_create_list();
RB_FOREACH(ch, &channel_name_tree, ch_name_link) { CHANNEL_FOREACH(ch) {
int num = numFollowing; int num = numFollowing;
RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) { RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
if (maxTime && e->start > maxTime) break; if (maxTime && e->start > maxTime) break;
@ -984,7 +984,7 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
/* Optional */ /* Optional */
if(!(htsmsg_get_u32(in, "channelId", &u32))) 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"); return htsp_error("Channel does not exist");
if(!(htsmsg_get_u32(in, "tagId", &u32))) if(!(htsmsg_get_u32(in, "tagId", &u32)))
if (!(ct = channel_tag_find_by_identifier(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)) if(htsmsg_get_s64(in, "stopExtra", &stop_extra))
stop_extra = 0; stop_extra = 0;
if(!htsmsg_get_u32(in, "channelId", &u32)) 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)) if(!htsmsg_get_u32(in, "eventId", &eventid))
e = epg_broadcast_find_by_id(eventid, ch); e = epg_broadcast_find_by_id(eventid, ch);
if(htsmsg_get_u32(in, "priority", &priority)) if(htsmsg_get_u32(in, "priority", &priority))
@ -1256,18 +1256,18 @@ htsp_method_subscribe(htsp_connection_t *htsp, htsmsg_t *in)
#if ENABLE_TIMESHIFT #if ENABLE_TIMESHIFT
uint32_t timeshiftPeriod = 0; uint32_t timeshiftPeriod = 0;
#endif #endif
const char *str;
channel_t *ch; channel_t *ch;
htsp_subscription_t *hs; htsp_subscription_t *hs;
const char *str;
if(htsmsg_get_u32(in, "subscriptionId", &sid)) if(htsmsg_get_u32(in, "subscriptionId", &sid))
return htsp_error("Missing argument 'subscriptionId'"); return htsp_error("Missing argument 'subscriptionId'");
if(!htsmsg_get_u32(in, "channelId", &chid)) { 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"); return htsp_error("Requested channel does not exist");
} else if((str = htsmsg_get_str(in, "channelName")) != NULL) { } 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"); return htsp_error("Requested channel does not exist");
} else { } else {
@ -2043,7 +2043,7 @@ htsp_channel_update_current(channel_t *ch)
m = htsmsg_create_map(); m = htsmsg_create_map();
htsmsg_add_str(m, "method", "channelUpdate"); 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; now = ch->ch_epg_now;
next = ch->ch_epg_next; next = ch->ch_epg_next;
@ -2086,7 +2086,7 @@ void
htsp_channel_delete(channel_t *ch) htsp_channel_delete(channel_t *ch)
{ {
htsmsg_t *m = htsmsg_create_map(); 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"); htsmsg_add_str(m, "method", "channelDelete");
htsp_async_send(m, HTSP_ASYNC_ON); htsp_async_send(m, HTSP_ASYNC_ON);
} }

View file

@ -206,6 +206,14 @@ idnode_delete(idnode_t *in)
* Info * 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); int idnode_insert(idnode_t *in, const char *uuid, const idclass_t *idc);
void idnode_unlink(idnode_t *in); 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); const char *idnode_uuid_as_str (const idnode_t *in);
idnode_set_t *idnode_get_childs (idnode_t *in); idnode_set_t *idnode_get_childs (idnode_t *in);
const char *idnode_get_title (idnode_t *in); const char *idnode_get_title (idnode_t *in);

View file

@ -49,7 +49,7 @@ tsfile_network_create_service
if (s) { if (s) {
char buf[128]; char buf[128];
sprintf(buf, "channel-%d", t); 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); service_mapper_link((service_t*)s, c);
t++; t++;
} }

View file

@ -701,7 +701,21 @@ main(int argc, char **argv)
service_init(); 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(); subscription_init();
@ -726,19 +740,6 @@ main(int argc, char **argv)
htsp_init(opt_bindaddr); 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) if(opt_subscribe != NULL)
subscription_dummy_join(opt_subscribe, 1); 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); htsmsg_add_u32(m, "nosave", 1);
if (pl->opts & PO_WRONCE) if (pl->opts & PO_WRONCE)
htsmsg_add_u32(m, "wronce", 1); htsmsg_add_u32(m, "wronce", 1);
if (pl->opts & PO_MULTI)
htsmsg_add_u32(m, "multi", 1);
/* Enum list */ /* Enum list */
if (pl->list) if (pl->list)

View file

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

View file

@ -49,24 +49,101 @@ static void service_class_save(struct idnode *self);
struct service_queue service_all; struct service_queue service_all;
#if 0 static const void *
static const void *service_class_channel_get(void *obj); 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)
{ {
channel_t *ch; service_t *svc = obj;
htsmsg_t *list = htsmsg_create_list(); channel_service_mapping_t *csm;
RB_FOREACH(ch, &channel_name_tree, ch_name_link) static char buf[2048], *s;
if (ch->ch_name) // TODO: make this dynamic length
htsmsg_add_str(list, NULL, ch->ch_name); int first = 1;
return list;
*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 = { const idclass_t service_class = {
.ic_class = "service", .ic_class = "service",
.ic_caption = "Service", .ic_caption = "Service",
.ic_save = service_class_save, .ic_save = service_class_save,
.ic_get_title = service_class_get_title,
.ic_properties = (const property_t[]){ .ic_properties = (const property_t[]){
{ {
.type = PT_BOOL, .type = PT_BOOL,
@ -74,16 +151,15 @@ const idclass_t service_class = {
.name = "Enabled", .name = "Enabled",
.off = offsetof(service_t, s_enabled), .off = offsetof(service_t, s_enabled),
}, },
#if 0
{ {
.type = PT_STR, .type = PT_STR,
.id = "channel", .id = "channel",
.name = "Channel", .name = "Channel",
.get = service_class_channel_get, .get = service_class_channel_get,
.set = service_class_channel_set, .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 * Find a service based on the given identifier
*/ */
service_t * service_t *
service_find_by_identifier(const char *identifier) service_find(const char *identifier)
{ {
return idnode_find(identifier, &service_class); return idnode_find(identifier, &service_class);
} }
@ -650,15 +726,18 @@ service_is_hdtv(service_t *t)
int int
service_is_radio(service_t *t) service_is_radio(service_t *t)
{ {
int ret = 0;
if (t->s_servicetype == ST_RADIO) if (t->s_servicetype == ST_RADIO)
return 1; return 1;
else if (t->s_servicetype == ST_NONE) { else if (t->s_servicetype == ST_NONE) {
elementary_stream_t *st; elementary_stream_t *st;
TAILQ_FOREACH(st, &t->s_components, es_link) TAILQ_FOREACH(st, &t->s_components, es_link)
if (SCT_ISAUDIO(st->es_type)) if (SCT_ISVIDEO(st->es_type))
return 1; 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; extern struct service_queue service_all;
struct channel;
/** /**
* Stream, one media component for a service. * Stream, one media component for a service.
*/ */
@ -232,6 +234,7 @@ typedef struct service {
*/ */
enum { enum {
ST_NONE, ST_NONE,
ST_OTHER,
ST_SDTV, ST_SDTV,
ST_HDTV, ST_HDTV,
ST_RADIO ST_RADIO
@ -433,7 +436,8 @@ void service_unref(service_t *t);
void service_ref(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, service_instance_t *service_find_instance(struct service *s,
struct channel *ch, 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_sdtv(service_t *t);
int service_is_hdtv(service_t *t); int service_is_hdtv(service_t *t);
int service_is_radio(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)) #define service_is_tv(s) (service_is_hdtv(s) || service_is_sdtv(s))
int service_is_encrypted ( service_t *t ); int service_is_encrypted ( service_t *t );

View file

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

View file

@ -42,7 +42,7 @@ void service_mapper_remove ( struct service *t );
int service_mapper_qlen ( void ); int service_mapper_qlen ( void );
// Link service to channel // 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 // Unlink service from channel
void service_mapper_unlink ( struct service *s, struct channel *c ); 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 / Queue header declarations
*/ */
LIST_HEAD(th_subscription_list, th_subscription); 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_config_list, dvr_config);
LIST_HEAD(dvr_entry_list, dvr_entry); LIST_HEAD(dvr_entry_list, dvr_entry);
TAILQ_HEAD(ref_update_queue, ref_update); TAILQ_HEAD(ref_update_queue, ref_update);
@ -481,7 +478,6 @@ int rate_to_sri(int rate);
extern time_t dispatch_clock; extern time_t dispatch_clock;
extern struct service_list all_transports; extern struct service_list all_transports;
extern struct channel_tree channel_name_tree;
extern void scopedunlock(pthread_mutex_t **mtxp); extern void scopedunlock(pthread_mutex_t **mtxp);

View file

@ -37,7 +37,6 @@
#include "channels.h" #include "channels.h"
#include "dvr/dvr.h" #include "dvr/dvr.h"
#include "service_mapper.h"
#include "epggrab.h" #include "epggrab.h"
#include "epg.h" #include "epg.h"
#include "muxer.h" #include "muxer.h"
@ -318,221 +317,6 @@ extjs_tablemgr(http_connection_t *hc, const char *remain, void *opaque)
return 0; 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 * EPG Content Groups
*/ */
@ -924,7 +708,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
m = htsmsg_create_map(); m = htsmsg_create_map();
htsmsg_add_str(m, "channel", ch->ch_name); 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) if(ch->ch_icon != NULL)
htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon); 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 *channel = http_arg_get(&hc->hc_req_args, "channelid");
const char *pri = http_arg_get(&hc->hc_req_args, "pri"); 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 || if(ch == NULL || title == NULL ||
datestr == NULL || strlen(datestr) != 10 || datestr == NULL || strlen(datestr) != 10 ||
@ -1653,49 +1437,6 @@ extjs_servicedetails(http_connection_t *hc,
return 0; 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 #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 * WEB user interface
*/ */
@ -2043,7 +1723,6 @@ extjs_start(void)
http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE); http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE);
http_path_add("/capabilities", NULL, extjs_capabilities, 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("/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("/epggrab", NULL, extjs_epggrab, ACCESS_WEB_INTERFACE);
http_path_add("/channeltags", NULL, extjs_channeltags, 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("/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("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE);
http_path_add("/config", NULL, extjs_config, 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("/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); http_path_add("/servicedetails", NULL, extjs_servicedetails, ACCESS_ADMIN);
#if ENABLE_TIMESHIFT #if ENABLE_TIMESHIFT
http_path_add("/timeshift", NULL, extjs_timeshift, ACCESS_ADMIN); http_path_add("/timeshift", NULL, extjs_timeshift, ACCESS_ADMIN);
#endif #endif
http_path_add("/tvhlog", NULL, extjs_tvhlog, ACCESS_ADMIN); http_path_add("/tvhlog", NULL, extjs_tvhlog, ACCESS_ADMIN);
http_path_add("/api/service_mapping", NULL, extjs_service_mapping, ACCESS_ADMIN);
#if ENABLE_V4L #if ENABLE_V4L
extjs_start_v4l(); extjs_start_v4l();
#endif #endif

View file

@ -55,7 +55,7 @@ dumpchannels(htsbuf_queue_t *hq)
channel_t *ch; channel_t *ch;
outputtitle(hq, 0, "Channels"); 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, "%s (%d)\n", ch->ch_name, ch->ch_id);
htsbuf_qprintf(hq, 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', [ 'name', 'chid', 'epggrabsrc', 'tags', 'ch_icon', 'epg_pre_start',
'epg_post_end', 'number' ]); 'epg_post_end', 'number' ]);
/*
tvheadend.channels = new Ext.data.JsonStore({ tvheadend.channels = new Ext.data.JsonStore({
autoLoad : true, autoLoad : true,
root : 'entries', root : 'entries',
@ -43,6 +44,7 @@ tvheadend.channels = new Ext.data.JsonStore({
tvheadend.comet.on('channels', function(m) { tvheadend.comet.on('channels', function(m) {
if (m.reload != null) tvheadend.channels.reload(); if (m.reload != null) tvheadend.channels.reload();
}); });
*/
/* /*
* Service mapping * Service mapping
@ -466,3 +468,24 @@ tvheadend.chconf = function() {
return grid; 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) { switch(f.type) {
case 'str': case 'str':
if (f.enum) { 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, fieldLabel : f.caption,
name : f.id, name : f.id,
value : f.value, value : f.value,
@ -124,7 +127,17 @@ tvheadend.idnode_editor_field = function(f, create)
typeAhead : true, typeAhead : true,
forceSelection : true, forceSelection : true,
triggerAction : 'all', 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 { } else {
return new Ext.form.TextField({ return new Ext.form.TextField({
@ -615,14 +628,24 @@ tvheadend.idnode_grid = function(panel, conf)
} }
}); });
buttons.push(editBtn); 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) { if (conf.help) {
buttons.push('->');
buttons.push({ buttons.push({
text : 'Help', text : 'Help',
handler : conf.help handler : conf.help
}); });
} }
/* Grid Panel */ /* Grid Panel */
var auto = new Ext.form.Checkbox({ var auto = new Ext.form.Checkbox({
checked : true, checked : true,

View file

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

View file

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