From 65c304fb9a4511e8526c534bee49ff12dbee2a61 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Tue, 20 Aug 2013 16:21:02 +0100 Subject: [PATCH] channel: migrated channel_t to an idnode Also since the channel name is no longer unique various other things have had to be updated. --- Makefile | 1 + src/api.c | 1 + src/api.h | 4 + src/api/api_channel.c | 74 +++ src/api/api_idnode.c | 24 +- src/api/api_service.c | 4 + src/channels.c | 746 ++++++++++++------------------ src/channels.h | 80 ++-- src/dvr/dvr_autorec.c | 6 +- src/dvr/dvr_db.c | 16 +- src/epg.c | 19 +- src/epgdb.c | 9 +- src/epggrab/channel.c | 10 + src/epggrab/module.c | 6 +- src/htsp_server.c | 30 +- src/idnode.c | 8 + src/idnode.h | 1 + src/input/mpegts/tsfile/tsfile.c | 2 +- src/main.c | 29 +- src/prop.c | 2 + src/prop.h | 1 + src/service.c | 115 ++++- src/service.h | 7 +- src/service_mapper.c | 19 +- src/service_mapper.h | 2 +- src/tvheadend.h | 4 - src/webui/extjs.c | 328 +------------ src/webui/statedump.c | 2 +- src/webui/static/app/chconf.js | 23 + src/webui/static/app/idnode.js | 29 +- src/webui/static/app/tvheadend.js | 2 +- src/webui/webui.c | 16 +- 32 files changed, 713 insertions(+), 907 deletions(-) create mode 100644 src/api/api_channel.c diff --git a/Makefile b/Makefile index 72a6ba71..f18334f7 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/src/api.c b/src/api.c index 20a5c775..2a0b4b67 100644 --- a/src/api.c +++ b/src/api.c @@ -118,4 +118,5 @@ void api_init ( void ) api_idnode_init(); api_mpegts_init(); api_service_init(); + api_channel_init(); } diff --git a/src/api.h b/src/api.h index 0933c36e..ca4be19e 100644 --- a/src/api.h +++ b/src/api.h @@ -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__ */ diff --git a/src/api/api_channel.c b/src/api/api_channel.c new file mode 100644 index 00000000..038c2296 --- /dev/null +++ b/src/api/api_channel.c @@ -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 . + */ + +#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__ */ diff --git a/src/api/api_idnode.c b/src/api/api_idnode.c index 42a5f1c8..3554bc2a 100644 --- a/src/api/api_idnode.c +++ b/src/api/api_idnode.c @@ -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"))) diff --git a/src/api/api_service.c b/src/api/api_service.c index 89a717f0..2f605534 100644 --- a/src/api/api_service.c +++ b/src/api/api_service.c @@ -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 }, }; diff --git a/src/channels.c b/src/channels.c index bc75e648..80d6983e 100644 --- a/src/channels.c +++ b/src/channels.c @@ -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(); } diff --git a/src/channels.h b/src/channels.h index 8a8af969..bb86f4d0 100644 --- a/src/channels.h +++ b/src/channels.h @@ -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 */ diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 57609ef8..f3384f99 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -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); diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index d6a1562c..48818faa 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -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); diff --git a/src/epg.c b/src/epg.c index e031ae07..141d040e 100644 --- a/src/epg.c +++ b/src/epg.c @@ -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); } diff --git a/src/epgdb.c b/src/epgdb.c index ebc1513b..a45d9e92 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -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"); diff --git a/src/epggrab/channel.c b/src/epggrab/channel.c index 3fe0381c..87cb6904 100644 --- a/src/epggrab/channel.c +++ b/src/epggrab/channel.c @@ -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 */ diff --git a/src/epggrab/module.c b/src/epggrab/module.c index 788dd8ab..a2bd9810 100644 --- a/src/epggrab/module.c +++ b/src/epggrab/module.c @@ -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); diff --git a/src/htsp_server.c b/src/htsp_server.c index 5d5d682e..70c32e71 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -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); } diff --git a/src/idnode.c b/src/idnode.c index c8e8c422..ff64bd69 100644 --- a/src/idnode.c +++ b/src/idnode.c @@ -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; +} + /** * */ diff --git a/src/idnode.h b/src/idnode.h index b90ada98..f456cab2 100644 --- a/src/idnode.h +++ b/src/idnode.h @@ -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); diff --git a/src/input/mpegts/tsfile/tsfile.c b/src/input/mpegts/tsfile/tsfile.c index ecd7d2a8..35ea683c 100644 --- a/src/input/mpegts/tsfile/tsfile.c +++ b/src/input/mpegts/tsfile/tsfile.c @@ -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++; } diff --git a/src/main.c b/src/main.c index c87830bf..2277c1e5 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/prop.c b/src/prop.c index 4488a2e6..0a2fd4ed 100644 --- a/src/prop.c +++ b/src/prop.c @@ -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) diff --git a/src/prop.h b/src/prop.h index 0e1f5256..f08d0855 100644 --- a/src/prop.h +++ b/src/prop.h @@ -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 diff --git a/src/service.c b/src/service.c index 43277ecf..61f6e1eb 100644 --- a/src/service.c +++ b/src/service.c @@ -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; } /** diff --git a/src/service.h b/src/service.h index c38491f4..62f6f593 100644 --- a/src/service.h +++ b/src/service.h @@ -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 ); diff --git a/src/service_mapper.c b/src/service_mapper.c index f4292466..00974bc3 100644 --- a/src/service_mapper.c +++ b/src/service_mapper.c @@ -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) { diff --git a/src/service_mapper.h b/src/service_mapper.h index 417ad7df..e30a5be1 100644 --- a/src/service_mapper.h +++ b/src/service_mapper.h @@ -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 ); diff --git a/src/tvheadend.h b/src/tvheadend.h index 1dcc6bbe..1fbba603 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -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); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index d014dff0..8f1a2327 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -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 diff --git a/src/webui/statedump.c b/src/webui/statedump.c index 3a30cd8f..1256c444 100644 --- a/src/webui/statedump.c +++ b/src/webui/statedump.c @@ -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, diff --git a/src/webui/static/app/chconf.js b/src/webui/static/app/chconf.js index bf1ba0aa..d04be40b 100644 --- a/src/webui/static/app/chconf.js +++ b/src/webui/static/app/chconf.js @@ -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 ] + }); +} diff --git a/src/webui/static/app/idnode.js b/src/webui/static/app/idnode.js index b339f11c..ed59c491 100644 --- a/src/webui/static/app/idnode.js +++ b/src/webui/static/app/idnode.js @@ -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, diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index a3280f2a..49ecb8f8 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -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 */ diff --git a/src/webui/webui.c b/src/webui/webui.c index 36ac4d07..1e17c8d2 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -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