diff --git a/Makefile b/Makefile index 850d0f54..a6786585 100644 --- a/Makefile +++ b/Makefile @@ -109,6 +109,11 @@ SRCS = src/version.c \ src/descrambler/descrambler.c \ src/service_mapper.c \ +SRCS += \ + src/api.c \ + src/api/api_idnode.c \ + src/api/api_mpegts.c \ + SRCS += \ src/parsers/parsers.c \ src/parsers/bitstream.c \ @@ -132,10 +137,10 @@ SRCS += src/dvr/dvr_db.c \ SRCS += src/webui/webui.c \ src/webui/comet.c \ src/webui/extjs.c \ - src/webui/extjs_dvb.c \ src/webui/simpleui.c \ src/webui/statedump.c \ src/webui/html.c\ + src/webui/webui_api.c\ SRCS += src/muxer.c \ src/muxer/muxer_pass.c \ @@ -203,11 +208,6 @@ SRCS-${CONFIG_TIMESHIFT} += \ SRCS-${CONFIG_INOTIFY} += \ src/dvr/dvr_inotify.c \ -# V4L -SRCS-${CONFIG_V4L} += \ - src/v4l.c \ - src/webui/extjs_v4l.c \ - # Avahi SRCS-$(CONFIG_AVAHI) += src/avahi.c diff --git a/src/api.c b/src/api.c new file mode 100644 index 00000000..529f6d8f --- /dev/null +++ b/src/api.c @@ -0,0 +1,120 @@ +/* + * API - Common functions for control/query API + * + * 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 . + */ + +#include "tvheadend.h" +#include "api.h" +#include "access.h" + +#include + +typedef struct api_link { + const api_hook_t *hook; + RB_ENTRY(api_link) link; +} api_link_t; + +RB_HEAD(,api_link) api_hook_tree; + +static int ah_cmp + ( api_link_t *a, api_link_t *b ) +{ + return strcmp(a->hook->ah_subsystem, b->hook->ah_subsystem); +} + +void +api_register ( const api_hook_t *hook ) +{ + static api_link_t *t, *skel = NULL; + if (!skel) + skel = calloc(1, sizeof(api_link_t)); + skel->hook = hook; + t = RB_INSERT_SORTED(&api_hook_tree, skel, link, ah_cmp); + if (t) + tvherror("api", "trying to re-register subsystem"); + else + skel = NULL; +} + +void +api_register_all ( const api_hook_t *hooks ) +{ + while (hooks->ah_subsystem) { + api_register(hooks); + hooks++; + } +} + +int +api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp ) +{ + api_hook_t h; + api_link_t *ah, skel; + const char *op; + + /* Args and response must be set */ + if (!args || !resp) + return EINVAL; + + // Note: there is no locking while checking the hook tree, its assumed + // this is all setup during init (if this changes the code will + // need updating) + h.ah_subsystem = subsystem; + skel.hook = &h; + ah = RB_FIND(&api_hook_tree, &skel, link, ah_cmp); + + if (!ah) { + tvhwarn("api", "failed to find subsystem [%s]", subsystem); + return ENOSYS; // TODO: is this really the right error code? + } + + /* Extract method */ + op = htsmsg_get_str(args, "method"); + if (!op) + op = htsmsg_get_str(args, "op"); + // Note: this is not required (so no final validation) + + /* Execute */ + return ah->hook->ah_callback(ah->hook->ah_opaque, op, args, resp); +} + +static int +api_serverinfo + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + *resp = htsmsg_create_map(); + htsmsg_add_str(*resp, "sw_version", tvheadend_version); + htsmsg_add_u32(*resp, "api_version", TVH_API_VERSION); + htsmsg_add_str(*resp, "name", "Tvheadend"); + if (tvheadend_webroot) + htsmsg_add_str(*resp, "webroot", tvheadend_webroot); + htsmsg_add_msg(*resp, "capabilities", tvheadend_capabilities_list(1)); + return 0; +} + +void api_init ( void ) +{ + static api_hook_t h[] = { + { "serverinfo", ACCESS_ANONYMOUS, api_serverinfo, NULL }, + { NULL, 0, NULL, NULL } + }; + api_register_all(h); + + /* Subsystems */ + api_idnode_init(); + api_mpegts_init(); +} diff --git a/src/api.h b/src/api.h new file mode 100644 index 00000000..8c69a73a --- /dev/null +++ b/src/api.h @@ -0,0 +1,87 @@ +/* + * API - Common functions for control/query API + * + * 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_H__ +#define __TVH_API_H__ + +#include "htsmsg.h" +#include "idnode.h" +#include "redblack.h" + +#define TVH_API_VERSION 12 + +/* + * Command hook + */ + +typedef int (*api_callback_t) + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ); + +typedef struct api_hook +{ + const char *ah_subsystem; + int ah_access; + api_callback_t ah_callback; + void *ah_opaque; +} api_hook_t; + +/* + * Regsiter handler + */ +void api_register ( const api_hook_t *hook ); +void api_register_all ( const api_hook_t *hooks ); + +/* + * Execute + */ +int api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp ); + +/* + * Initialise + */ +void api_init ( void ); +void api_idnode_init ( void ); +void api_mpegts_init ( void ); + +/* + * IDnode + */ +typedef struct api_idnode_grid_conf +{ + int start; + int limit; + idnode_filter_t filter; + idnode_sort_t sort; +} api_idnode_grid_conf_t; + +typedef void (*api_idnode_grid_callback_t) + (idnode_set_t*, api_idnode_grid_conf_t*); +typedef idnode_set_t *(*api_idnode_tree_callback_t) + (void); + +int api_idnode_grid + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ); + +int api_idnode_class + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ); + +int api_idnode_tree + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ); + +#endif /* __TVH_API_H__ */ diff --git a/src/api/api_idnode.c b/src/api/api_idnode.c new file mode 100644 index 00000000..9ba3eccb --- /dev/null +++ b/src/api/api_idnode.c @@ -0,0 +1,438 @@ +/* + * API - idnode related API 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_IDNODE_H__ +#define __TVH_API_IDNODE_H__ + +#include "tvheadend.h" +#include "access.h" +#include "idnode.h" +#include "htsmsg.h" +#include "api.h" + +static struct strtab filtcmptab[] = { + { "gt", IC_GT }, + { "lt", IC_LT }, + { "eq", IC_EQ } +}; + +static void +api_idnode_grid_conf + ( htsmsg_t *args, api_idnode_grid_conf_t *conf ) +{ + htsmsg_field_t *f; + htsmsg_t *filter, *e; + const char *str; + + /* Start */ + if ((str = htsmsg_get_str(args, "start"))) + conf->start = atoi(str); + else + conf->start = 0; + + /* Limit */ + if ((str = htsmsg_get_str(args, "limit"))) + conf->limit = atoi(str); + else + conf->limit = 50; + + /* Filter */ + if ((filter = htsmsg_get_list(args, "filter"))) { + HTSMSG_FOREACH(f, filter) { + const char *k, *t, *v; + if (!(e = htsmsg_get_map_by_field(f))) continue; + if (!(k = htsmsg_get_str(e, "field"))) continue; + if (!(t = htsmsg_get_str(e, "type"))) continue; + if (!strcmp(t, "string")) { + if ((v = htsmsg_get_str(e, "value"))) + idnode_filter_add_str(&conf->filter, k, v, IC_RE); + } else if (!strcmp(t, "numeric")) { + uint32_t v; + if (!htsmsg_get_u32(e, "value", &v)) { + int t = str2val(htsmsg_get_str(e, "comparison") ?: "", + filtcmptab); + idnode_filter_add_num(&conf->filter, k, v, t == -1 ? IC_EQ : t); + } + } else if (!strcmp(t, "boolean")) { + uint32_t v; + if (!htsmsg_get_u32(e, "value", &v)) + idnode_filter_add_bool(&conf->filter, k, v, IC_EQ); + } + } + } + + /* Sort */ + if ((str = htsmsg_get_str(args, "sort"))) { + conf->sort.key = str; + if ((str = htsmsg_get_str(args, "dir")) && !strcmp(str, "DESC")) + conf->sort.dir = IS_DSC; + else + conf->sort.dir = IS_ASC; + } else + conf->sort.key = NULL; +} + +int +api_idnode_grid + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int i; + htsmsg_t *list, *e; + api_idnode_grid_conf_t conf = { 0 }; + idnode_set_t ins = { 0 }; + api_idnode_grid_callback_t cb = opaque; + + /* Grid configuration */ + api_idnode_grid_conf(args, &conf); + + /* Create list */ + pthread_mutex_lock(&global_lock); + cb(&ins, &conf); + + /* Sort */ + if (conf.sort.key) + idnode_set_sort(&ins, &conf.sort); + + /* Paginate */ + list = htsmsg_create_list(); + for (i = conf.start; i < ins.is_count && conf.limit != 0; i++) { + e = htsmsg_create_map(); + htsmsg_add_str(e, "uuid", idnode_uuid_as_str(ins.is_array[i])); + idnode_read0(ins.is_array[i], e, 0); + htsmsg_add_msg(list, NULL, e); + if (conf.limit > 0) conf.limit--; + } + + pthread_mutex_unlock(&global_lock); + + /* Output */ + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", list); + htsmsg_add_u32(*resp, "total", ins.is_count); + + /* Cleanup */ + free(ins.is_array); + idnode_filter_clear(&conf.filter); + + return 0; +} + +static int +api_idnode_load_by_class + ( const char *class, htsmsg_t *args, htsmsg_t **resp ) +{ + int i, _enum; + const idclass_t *idc; + idnode_set_t *is; + idnode_t *in; + htsmsg_t *l, *e; + + // TODO: this only works if pass as integer + _enum = htsmsg_get_bool_or_default(args, "enum", 0); + + pthread_mutex_lock(&global_lock); + + /* Find class */ + if (!(idc = idclass_find(class))) { + pthread_mutex_unlock(&global_lock); + return EINVAL; + } + + l = htsmsg_create_list(); + if ((is = idnode_find_all(idc))) { + for (i = 0; i < is->is_count; i++) { + in = is->is_array[i]; + + /* Name/UUID only */ + if (_enum) { + e = htsmsg_create_map(); + htsmsg_add_str(e, "key", idnode_uuid_as_str(in)); + htsmsg_add_str(e, "val", idnode_get_title(in)); + + /* Full record */ + } else + e = idnode_serialize(in); + + if (e) + htsmsg_add_msg(l, NULL, e); + } + } + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", l); + + pthread_mutex_unlock(&global_lock); + + return 0; +} + +static int +api_idnode_load + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int err = 0; + idnode_t *in; + htsmsg_t *t, *l = NULL; + htsmsg_field_t *f; + const char *uuid, *class; + + /* Class based */ + if ((class = htsmsg_get_str(args, "class"))) + return api_idnode_load_by_class(class, args, resp); + + /* ID based */ + if (!(f = htsmsg_field_find(args, "uuid"))) + return EINVAL; + + pthread_mutex_lock(&global_lock); + + /* Single */ + if (f->hmf_type == HMF_STR) { + uuid = htsmsg_field_get_string(f); + in = idnode_find(uuid, NULL); + if (in) { + l = htsmsg_create_list(); + htsmsg_add_msg(l, NULL, idnode_serialize(in)); + } else + err = ENOENT; + + /* Multiple */ + } else if (f->hmf_type == HMF_LIST) { + t = htsmsg_get_list_by_field(f); + l = htsmsg_create_list(); + HTSMSG_FOREACH(f, t) { + if (!(uuid = htsmsg_field_get_string(f))) continue; + if (!(in = idnode_find(uuid, NULL))) continue; + htsmsg_add_msg(l, NULL, idnode_serialize(in)); + } + + /* Invalid */ + } else { + err = EINVAL; + } + + if (l) { + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", l); + } + + pthread_mutex_unlock(&global_lock); + + return err; +} + +static int +api_idnode_save + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int err = EINVAL; + idnode_t *in; + htsmsg_t *conf, *l; + htsmsg_field_t *f; + const char *uuid; + + if (!(f = htsmsg_field_find(args, "node"))) + return EINVAL; + + pthread_mutex_lock(&global_lock); + + /* Single */ + if (f->hmf_type == HMF_MAP) { + conf = htsmsg_get_map_by_field(f); + if (!(uuid = htsmsg_get_str(conf, "uuid"))) + goto exit; + if (!(in = idnode_find(uuid, NULL))) + goto exit; + idnode_update(in, conf); + err = 0; + + /* Multiple */ + } else if (f->hmf_type == HMF_LIST) { + l = htsmsg_get_list_by_field(f); + HTSMSG_FOREACH(f, l) { + if (!(conf = htsmsg_get_map_by_field(f))) + continue; + if (!(uuid = htsmsg_get_str(conf, "uuid"))) + continue; + if (!(in = idnode_find(uuid, NULL))) + continue; + idnode_update(in, conf); + } + err = 0; + + /* Invalid */ + } else { + err = EINVAL; + } + + // TODO: return updated UUIDs? + +exit: + pthread_mutex_unlock(&global_lock); + + return err; +} + +int +api_idnode_tree + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + const char *uuid; + const char *root = NULL; + int isroot; + idnode_t *node = NULL; + api_idnode_tree_callback_t rootfn = opaque; + + /* UUID */ + if (!(uuid = htsmsg_get_str(args, "uuid"))) + return EINVAL; + + /* Root UUID */ + if (!rootfn) + root = htsmsg_get_str(args, "root"); + + /* Is root? */ + isroot = (strcmp("root", uuid) == 0); + if (isroot && !(root || rootfn)) + return EINVAL; + + pthread_mutex_lock(&global_lock); + + if (!isroot || root) { + if (!(node = idnode_find(isroot ? root : uuid, NULL))) { + pthread_mutex_unlock(&global_lock); + return EINVAL; + } + } + + *resp = htsmsg_create_list(); + + /* Root node */ + if (isroot && node) { + htsmsg_t *m = idnode_serialize(node); + htsmsg_add_u32(m, "leaf", idnode_is_leaf(node)); + htsmsg_add_msg(*resp, NULL, m); + + /* Children */ + } else { + idnode_set_t *v = node ? idnode_get_childs(node) : rootfn(); + if (v) { + int i; + for(i = 0; i < v->is_count; i++) { + htsmsg_t *m = idnode_serialize(v->is_array[i]); + htsmsg_add_u32(m, "leaf", idnode_is_leaf(v->is_array[i])); + htsmsg_add_msg(*resp, NULL, m); + } + idnode_set_free(v); + } + } + pthread_mutex_unlock(&global_lock); + + return 0; +} + +int +api_idnode_class + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int err = EINVAL; + const char *name; + const idclass_t *idc; + + pthread_mutex_lock(&global_lock); + + /* Lookup */ + if (!opaque) { + if (!(name = htsmsg_get_str(args, "name"))) + goto exit; + if (!(idc = idclass_find(name))) + goto exit; + + } else { + idc = opaque; + } + + err = 0; + *resp = idclass_serialize(idc); + +exit: + pthread_mutex_unlock(&global_lock); + + return err; +} + +static int +api_idnode_delete + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int err = 0; + idnode_t *in; + htsmsg_t *l; + htsmsg_field_t *f; + const char *uuid; + + /* ID based */ + if (!(f = htsmsg_field_find(args, "uuid"))) + return EINVAL; + + pthread_mutex_lock(&global_lock); + + /* Single */ + if (f->hmf_type == HMF_STR) { + uuid = htsmsg_field_get_string(f); + in = idnode_find(uuid, NULL); + if (in) { + idnode_delete(in); + } else + err = ENOENT; + + /* Multiple */ + } else if (f->hmf_type == HMF_LIST) { + l = htsmsg_get_list_by_field(f); + HTSMSG_FOREACH(f, l) { + if (!(uuid = htsmsg_field_get_string(f))) continue; + if (!(in = idnode_find(uuid, NULL))) continue; + idnode_delete(in); + } + } + + // TODO: should we return the UUIDs that are deleted? + if (!err) + *resp = htsmsg_create_map(); + + pthread_mutex_unlock(&global_lock); + + return err; +} + +void api_idnode_init ( void ) +{ + static api_hook_t ah[] = { + { "idnode/load", ACCESS_ANONYMOUS, api_idnode_load, NULL }, + { "idnode/save", ACCESS_ADMIN, api_idnode_save, NULL }, + { "idnode/tree", ACCESS_ANONYMOUS, api_idnode_tree, NULL }, + { "idnode/class", ACCESS_ANONYMOUS, api_idnode_class, NULL }, + { "idnode/delete", ACCESS_ADMIN, api_idnode_delete, NULL }, + { NULL }, + }; + + api_register_all(ah); +} + + +#endif /* __TVH_API_IDNODE_H__ */ diff --git a/src/api/api_mpegts.c b/src/api/api_mpegts.c new file mode 100644 index 00000000..a20e6ae3 --- /dev/null +++ b/src/api/api_mpegts.c @@ -0,0 +1,324 @@ +/* + * tvheadend - API access to MPEGTS system + * + * 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 . + */ + +#include "tvheadend.h" +#include "access.h" +#include "htsmsg.h" +#include "api.h" +#include "input/mpegts.h" +#if ENABLE_LINUXDVB +#include "input/mpegts/linuxdvb.h" +#include "input/mpegts/linuxdvb/linuxdvb_private.h" +#endif + +/* + * Inputs + */ +static int +api_mpegts_input_network_list + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int i, err = EINVAL; + const char *uuid; + mpegts_input_t *mi; + mpegts_network_t *mn; + idnode_set_t *is; + extern const idclass_t mpegts_input_class; + + if (!(uuid = htsmsg_get_str(args, "uuid"))) + return EINVAL; + + pthread_mutex_lock(&global_lock); + + mi = mpegts_input_find(uuid); + if (!mi) + goto exit; + + htsmsg_t *l = htsmsg_create_list(); + if ((is = mi->mi_network_list(mi))) { + for (i = 0; i < is->is_count; i++) { + char buf[256]; + htsmsg_t *e = htsmsg_create_map(); + mn = (mpegts_network_t*)is->is_array[i]; + htsmsg_add_str(e, "key", idnode_uuid_as_str(is->is_array[i])); + mn->mn_display_name(mn, buf, sizeof(buf)); + htsmsg_add_str(e, "val", buf); + htsmsg_add_msg(l, NULL, e); + } + idnode_set_free(is); + } + err = 0; + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", l); + +exit: + pthread_mutex_unlock(&global_lock); + + return err; +} + +/* + * Networks + */ +static void +api_mpegts_network_grid + ( idnode_set_t *ins, api_idnode_grid_conf_t *conf ) +{ + mpegts_network_t *mn; + + LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) { + idnode_set_add(ins, (idnode_t*)mn, &conf->filter); + } +} + +static int +api_mpegts_network_builders + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + mpegts_network_builder_t *mnb; + htsmsg_t *l, *e; + + /* List of available builder classes */ + l = htsmsg_create_list(); + LIST_FOREACH(mnb, &mpegts_network_builders, link) + if ((e = idclass_serialize(mnb->idc))) + htsmsg_add_msg(l, NULL, e); + + /* Output */ + *resp = htsmsg_create_map(); + htsmsg_add_msg(*resp, "entries", l); + + return 0; +} + +static int +api_mpegts_network_create + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int err; + const char *class; + htsmsg_t *conf; + mpegts_network_t *mn; + + if (!(class = htsmsg_get_str(args, "class"))) + return -EINVAL; + if (!(conf = htsmsg_get_map(args, "conf"))) + return -EINVAL; + + pthread_mutex_lock(&global_lock); + mn = mpegts_network_build(class, conf); + if (mn) { + err = 0; + *resp = htsmsg_create_map(); + mn->mn_config_save(mn); + } else { + err = -EINVAL; + } + pthread_mutex_unlock(&global_lock); + + return err; +} + +static int +api_mpegts_network_muxclass + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int err = EINVAL; + const idclass_t *idc; + mpegts_network_t *mn; + const char *uuid; + + if (!(uuid = htsmsg_get_str(args, "uuid"))) + return EINVAL; + + pthread_mutex_lock(&global_lock); + + if (!(mn = mpegts_network_find(uuid))) + goto exit; + + if (!(idc = mn->mn_mux_class(mn))) + goto exit; + + *resp = idclass_serialize(idc); + err = 0; + +exit: + pthread_mutex_unlock(&global_lock); + return err; +} + +static int +api_mpegts_network_muxcreate + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int err = EINVAL; + mpegts_network_t *mn; + mpegts_mux_t *mm; + htsmsg_t *conf; + const char *uuid; + + if (!(uuid = htsmsg_get_str(args, "uuid"))) + return EINVAL; + if (!(conf = htsmsg_get_map(args, "conf"))) + return EINVAL; + + pthread_mutex_lock(&global_lock); + + if (!(mn = mpegts_network_find(uuid))) + goto exit; + + if (!(mm = mn->mn_mux_create2(mn, conf))) + goto exit; + + mm->mm_config_save(mm); + err = 0; + +exit: + pthread_mutex_unlock(&global_lock); + return err; +} + +/* + * Muxes + */ +static void +api_mpegts_mux_grid + ( idnode_set_t *ins, api_idnode_grid_conf_t *conf ) +{ + mpegts_network_t *mn; + mpegts_mux_t *mm; + + LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) { + LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link) { + idnode_set_add(ins, (idnode_t*)mm, &conf->filter); + } + } +} + +/* + * Services + */ +static void +api_mpegts_service_grid + ( idnode_set_t *ins, api_idnode_grid_conf_t *conf ) +{ + mpegts_network_t *mn; + mpegts_mux_t *mm; + mpegts_service_t *ms; + + LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) { + LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link) { + LIST_FOREACH(ms, &mm->mm_services, s_dvb_mux_link) { + idnode_set_add(ins, (idnode_t*)ms, &conf->filter); + } + } + } +} + +/* + * Satconfs + */ +#if ENABLE_LINUXDVB +static void +api_linuxdvb_satconf_grid + ( idnode_set_t *ins, api_idnode_grid_conf_t *conf ) +{ + mpegts_input_t *mi; + extern const idclass_t linuxdvb_satconf_class; + + LIST_FOREACH(mi, &mpegts_input_all, mi_global_link) + if (idnode_is_instance((idnode_t*)mi, &linuxdvb_satconf_class)) + idnode_set_add(ins, (idnode_t*)mi, &conf->filter); +} + +static int +api_linuxdvb_satconf_create + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + int err; + htsmsg_t *conf; + idnode_t *in; + + if (!(conf = htsmsg_get_map(args, "conf"))) + return -EINVAL; + + pthread_mutex_lock(&global_lock); + in = (idnode_t*)linuxdvb_satconf_create0(NULL, conf); + if (in) { + err = 0; + in->in_class->ic_save(in); + *resp = htsmsg_create_map(); + } else { + err = -EINVAL; + } + pthread_mutex_unlock(&global_lock); + + return err; +} +#endif + +/* + * Adapter list + * + * TODO: this will need reworking for mpegps etc... + */ +static idnode_set_t * +api_tvadapter_tree ( void ) +{ +#if ENABLE_LINUXDVB + return linuxdvb_root(); +#else + return NULL; +#endif +} + +/* + * Init + */ +void +api_mpegts_init ( void ) +{ + extern const idclass_t mpegts_network_class; + extern const idclass_t mpegts_mux_class; + extern const idclass_t mpegts_service_class; + extern const idclass_t linuxdvb_satconf_class; + + static api_hook_t ah[] = { + { "tvadapter/tree", ACCESS_ANONYMOUS, api_idnode_tree, api_tvadapter_tree }, + { "mpegts/input/network_list", ACCESS_ANONYMOUS, api_mpegts_input_network_list, NULL }, + { "mpegts/network/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_mpegts_network_grid }, + { "mpegts/network/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_network_class }, + { "mpegts/network/builders", ACCESS_ANONYMOUS, api_mpegts_network_builders, NULL }, + { "mpegts/network/create", ACCESS_ANONYMOUS, api_mpegts_network_create, NULL }, + { "mpegts/network/mux_class", ACCESS_ANONYMOUS, api_mpegts_network_muxclass, NULL }, + { "mpegts/network/mux_create", ACCESS_ANONYMOUS, api_mpegts_network_muxcreate, NULL }, + { "mpegts/mux/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_mpegts_mux_grid }, + { "mpegts/mux/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_mux_class }, + { "mpegts/service/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_mpegts_service_grid }, + { "mpegts/service/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&mpegts_service_class }, +#if ENABLE_LINUXDVB + { "linuxdvb/satconf/grid", ACCESS_ANONYMOUS, api_idnode_grid, api_linuxdvb_satconf_grid }, + { "linuxdvb/satconf/class", ACCESS_ANONYMOUS, api_idnode_class, (void*)&linuxdvb_satconf_class }, + { "linuxdvb/satconf/create", ACCESS_ANONYMOUS, api_linuxdvb_satconf_create, NULL }, +#endif + { NULL }, + }; + + api_register_all(ah); +} diff --git a/src/idnode.c b/src/idnode.c index b53a270c..b6d4b0f7 100644 --- a/src/idnode.c +++ b/src/idnode.c @@ -183,6 +183,23 @@ idnode_unlink(idnode_t *in) idnode_notify(in, NULL, 0, 1); } +/** + * + */ +void +idnode_delete(idnode_t *in) +{ + lock_assert(&global_lock); + const idclass_t *idc = in->in_class; + while (idc) { + if (idc->ic_delete) { + idc->ic_delete(in); + break; + } + idc = idc->ic_super; + } +} + /* ************************************************************************** * Info * *************************************************************************/ diff --git a/src/idnode.h b/src/idnode.h index 9730fbac..b90ada98 100644 --- a/src/idnode.h +++ b/src/idnode.h @@ -52,6 +52,7 @@ typedef struct idclass { idnode_set_t *(*ic_get_childs)(idnode_t *self); const char *(*ic_get_title) (idnode_t *self); void (*ic_save) (idnode_t *self); + void (*ic_delete) (idnode_t *self); } idclass_t; /* @@ -114,6 +115,7 @@ idnode_set_t *idnode_get_childs (idnode_t *in); const char *idnode_get_title (idnode_t *in); int idnode_is_leaf (idnode_t *in); int idnode_is_instance (idnode_t *in, const idclass_t *idc); +void idnode_delete (idnode_t *in); void *idnode_find (const char *uuid, const idclass_t *idc); idnode_set_t *idnode_find_all(const idclass_t *idc); diff --git a/src/input/mpegts.h b/src/input/mpegts.h index 116c3af3..59bb3086 100644 --- a/src/input/mpegts.h +++ b/src/input/mpegts.h @@ -177,7 +177,6 @@ struct mpegts_network /* * Functions */ - void (*mn_delete) (mpegts_network_t *mn); void (*mn_display_name) (mpegts_network_t*, char *buf, size_t len); void (*mn_config_save) (mpegts_network_t*); mpegts_mux_t* (*mn_create_mux) @@ -472,9 +471,6 @@ extern const idclass_t mpegts_network_class; #define mpegts_network_find(u)\ idnode_find(u, &mpegts_network_class) -#define mpegts_network_delete_by_uuid(u)\ - { mpegts_network_t *mn = mpegts_network_find(u); if (mn && mn->mn_delete) mn->mn_delete(mn); } - void mpegts_network_delete ( mpegts_network_t *mn ); void mpegts_network_schedule_initial_scan diff --git a/src/input/mpegts/linuxdvb/linuxdvb_frontend.c b/src/input/mpegts/linuxdvb/linuxdvb_frontend.c index aff55313..2110f8ff 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_frontend.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_frontend.c @@ -95,8 +95,7 @@ linuxdvb_frontend_class_network_enum(void *o) htsmsg_t *m = htsmsg_create_map(); htsmsg_t *p = htsmsg_create_map(); htsmsg_add_str(m, "type", "api"); - htsmsg_add_str(m, "uri", "mpegts/input"); - htsmsg_add_str(p, "op", "network_list"); + htsmsg_add_str(m, "uri", "mpegts/input/network_list"); htsmsg_add_str(p, "uuid", idnode_uuid_as_str((idnode_t*)o)); htsmsg_add_str(m, "event", "mpegts_network"); htsmsg_add_msg(m, "params", p); diff --git a/src/input/mpegts/linuxdvb/linuxdvb_network.c b/src/input/mpegts/linuxdvb/linuxdvb_network.c index e3d11eba..1ea523cf 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_network.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_network.c @@ -36,11 +36,25 @@ extern const idclass_t mpegts_network_class; +static void +linuxdvb_network_class_delete ( idnode_t *in ) +{ + mpegts_network_t *mn = (mpegts_network_t*)in; + + /* remove config */ + hts_settings_remove("input/linuxdvb/networks/%s", + idnode_uuid_as_str(in)); + + /* Parent delete */ + mpegts_network_delete(mn); +} + const idclass_t linuxdvb_network_class = { .ic_super = &mpegts_network_class, .ic_class = "linuxdvb_network", .ic_caption = "LinuxDVB Network", + .ic_delete = linuxdvb_network_class_delete, .ic_properties = (const property_t[]){ {} } @@ -169,18 +183,6 @@ linuxdvb_network_mux_create2 NULL, NULL, conf); } -static void -linuxdvb_network_delete - ( mpegts_network_t *mn ) -{ - /* remove config */ - hts_settings_remove("input/linuxdvb/networks/%s", - idnode_uuid_as_str(&mn->mn_id)); - - /* Parent delete */ - mpegts_network_delete(mn); -} - /* **************************************************************************** * Creation/Config * ***************************************************************************/ @@ -208,7 +210,6 @@ linuxdvb_network_create0 ln->ln_type = FE_ATSC; /* Callbacks */ - ln->mn_delete = linuxdvb_network_delete; ln->mn_create_mux = linuxdvb_network_create_mux; ln->mn_create_service = linuxdvb_network_create_service; ln->mn_config_save = linuxdvb_network_config_save; diff --git a/src/input/mpegts/linuxdvb/linuxdvb_satconf.c b/src/input/mpegts/linuxdvb/linuxdvb_satconf.c index 96b3b2e0..823fa765 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_satconf.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_satconf.c @@ -77,9 +77,9 @@ linuxdvb_satconf_class_network_enum(void *o) htsmsg_t *m = htsmsg_create_map(); htsmsg_t *p = htsmsg_create_map(); htsmsg_add_str(m, "type", "api"); - htsmsg_add_str(m, "uri", "idnode"); + htsmsg_add_str(m, "uri", "idnode/load"); htsmsg_add_str(m, "event", "mpegts_network"); - htsmsg_add_str(p, "op", "list"); + htsmsg_add_u32(p, "enum", 1); htsmsg_add_str(p, "class", linuxdvb_network_dvbs_class.ic_class); htsmsg_add_msg(m, "params", p); diff --git a/src/main.c b/src/main.c index 09e51f84..c87830bf 100644 --- a/src/main.c +++ b/src/main.c @@ -37,6 +37,7 @@ #include #include "tvheadend.h" +#include "api.h" #include "tcp.h" #include "access.h" #include "http.h" @@ -687,6 +688,8 @@ main(int argc, char **argv) /** * Initialize subsystems */ + + api_init(); #if ENABLE_LIBAV libav_init(); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 6789293c..d014dff0 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1885,161 +1885,6 @@ extjs_tvhlog(http_connection_t *hc, const char *remain, void *opaque) return 0; } -static int -extjs_idnode_tree - ( http_connection_t *hc, const char *uuid, const char *root, - idnode_set_t *(*rootfn)(void), htsmsg_t **out ) -{ - int isroot; - idnode_t *node = NULL; - - /* Validate */ - if (!uuid) - return HTTP_STATUS_BAD_REQUEST; - isroot = !strcmp("root", uuid); - if (isroot && !(root || rootfn)) - return HTTP_STATUS_BAD_REQUEST; - - pthread_mutex_lock(&global_lock); - - if (!isroot || root) { - if (!(node = idnode_find(isroot ? root : uuid, NULL))) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_BAD_REQUEST; - } - } - - *out = htsmsg_create_list(); - - /* Root node */ - if (isroot && node) { - htsmsg_t *m = idnode_serialize(node); - htsmsg_add_u32(m, "leaf", idnode_is_leaf(node)); - htsmsg_add_msg(*out, NULL, m); - - /* Children */ - } else { - idnode_set_t *v = node ? idnode_get_childs(node) : rootfn(); - if (v) { - int i; - for(i = 0; i < v->is_count; i++) { - htsmsg_t *m = idnode_serialize(v->is_array[i]); - htsmsg_add_u32(m, "leaf", idnode_is_leaf(v->is_array[i])); - htsmsg_add_msg(*out, NULL, m); - } - idnode_set_free(v); - } - } - pthread_mutex_unlock(&global_lock); - - return 0; -} - -static int -extjs_idnode0 - (http_connection_t *hc, const char *remain, void *opaque, - idnode_set_t *(*rootfn)(void)) -{ - htsbuf_queue_t *hq = &hc->hc_reply; - htsmsg_t *out = NULL; - idnode_t *node = NULL; - const char *uuid, *root, *op = http_arg_get(&hc->hc_req_args, "op"); - - if (!op) return HTTP_STATUS_BAD_REQUEST; - - /* Get details */ - if (!strcmp(op, "get")) { - if (!(uuid = http_arg_get(&hc->hc_req_args, "uuid"))) - return HTTP_STATUS_BAD_REQUEST; - pthread_mutex_lock(&global_lock); - if (!(node = idnode_find(uuid, NULL))) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_BAD_REQUEST; - } - out = htsmsg_create_map(); - htsmsg_t *m = idnode_serialize(node); - pthread_mutex_unlock(&global_lock); - htsmsg_add_u32(m, "leaf", idnode_is_leaf(node)); - htsmsg_add_msg(out, "nodes", m); - - /* Update */ - } else if (!strcmp(op, "save")) { - const char *s; - htsmsg_field_t *f; - htsmsg_t *conf, *nodes; - if ((s = http_arg_get(&hc->hc_req_args, "nodes"))) { - if ((nodes = htsmsg_json_deserialize(s))) { - pthread_mutex_lock(&global_lock); - HTSMSG_FOREACH(f, nodes) { - if (!(conf = htsmsg_get_map_by_field(f))) continue; - if (!(uuid = htsmsg_get_str(conf, "uuid"))) continue; - if (!(node = idnode_find(uuid, NULL))) continue; - idnode_update(node, conf); - } - pthread_mutex_unlock(&global_lock); - htsmsg_destroy(nodes); - } - } - out = htsmsg_create_map(); - - /* List by class */ - } else if (!strcmp(op, "list")) { - int i; - const char *cls = http_arg_get(&hc->hc_req_args, "class"); - pthread_mutex_lock(&global_lock); - const idclass_t *idc = idclass_find(cls); - idnode_set_t *is = idnode_find_all(idc); - out = htsmsg_create_map(); - if (is) { - htsmsg_t *l = htsmsg_create_list(); - for (i = 0; i < is->is_count; i++) { - idnode_t *in = is->is_array[i]; - htsmsg_t *e = htsmsg_create_map(); - htsmsg_add_str(e, "key", idnode_uuid_as_str(in)); - htsmsg_add_str(e, "val", idnode_get_title(in)); - htsmsg_add_msg(l, NULL, e); - } - idnode_set_free(is); - htsmsg_add_msg(out, "entries", l); - } - pthread_mutex_unlock(&global_lock); - - /* Children */ - } else if (!strcmp(op, "childs")) { - int e; - uuid = http_arg_get(&hc->hc_req_args, "uuid"); - root = http_arg_get(&hc->hc_req_args, "root"); - if ((e = extjs_idnode_tree(hc, uuid, root, rootfn, &out))) - return e; - } - - if (!out) - 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; -} - -static int -extjs_idnode - (http_connection_t *hc, const char *remain, void *opaque) -{ - return extjs_idnode0(hc, remain, opaque, NULL); -} - -/** - * - */ -static int -extjs_tvadapters(http_connection_t *hc, const char *remain, void *opaque) -{ - return extjs_idnode0(hc, remain, opaque, &linuxdvb_root); -} - - - /** * Capability check */ @@ -2221,13 +2066,8 @@ extjs_start(void) #endif http_path_add("/tvhlog", NULL, extjs_tvhlog, ACCESS_ADMIN); - http_path_add("/tvadapters", - NULL, extjs_tvadapters, ACCESS_ADMIN); - http_path_add("/api/idnode", NULL, extjs_idnode, ACCESS_ADMIN); // TODO: might want diff access for read/write` http_path_add("/api/service_mapping", NULL, extjs_service_mapping, ACCESS_ADMIN); - extjs_start_dvb(); - #if ENABLE_V4L extjs_start_v4l(); #endif diff --git a/src/webui/extjs_dvb.c b/src/webui/extjs_dvb.c deleted file mode 100644 index d1a1cc56..00000000 --- a/src/webui/extjs_dvb.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * tvheadend, EXTJS based interface - * Copyright (C) 2008 Andreas Öman - * - * 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 . - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "htsmsg.h" -#include "htsmsg_json.h" - -#include "tvheadend.h" -#include "http.h" -#include "webui.h" -#include "access.h" -#include "dtable.h" -#include "channels.h" - -#include "input.h" - -extern const idclass_t mpegts_input_class; -extern const idclass_t mpegts_network_class; -extern const idclass_t mpegts_mux_class; -extern const idclass_t mpegts_service_class; - -typedef struct extjs_grid_conf -{ - int start; - int limit; - idnode_filter_t filter; - idnode_sort_t sort; -} extjs_grid_conf_t; - -static struct strtab extjs_filtcmp_tab[] = { - { "gt", IC_GT }, - { "lt", IC_LT }, - { "eq", IC_EQ } -}; - -static void -extjs_grid_conf - ( struct http_arg_list *args, extjs_grid_conf_t *conf ) -{ - const char *str; - if ((str = http_arg_get(args, "start"))) - conf->start = atoi(str); - else - conf->start = 0; - if ((str = http_arg_get(args, "limit"))) - conf->limit = atoi(str); - else - conf->limit = 50; - if ((str = http_arg_get(args, "filter"))) { - htsmsg_field_t *f; - htsmsg_t *e, *t = htsmsg_json_deserialize(str); - HTSMSG_FOREACH(f, t) { - const char *k, *t, *v; - if (!(e = htsmsg_get_map_by_field(f))) continue; - if (!(k = htsmsg_get_str(e, "field"))) continue; - if (!(t = htsmsg_get_str(e, "type"))) continue; - if (!strcmp(t, "string")) { - if ((v = htsmsg_get_str(e, "value"))) - idnode_filter_add_str(&conf->filter, k, v, IC_RE); - } else if (!strcmp(t, "numeric")) { - uint32_t v; - if (!htsmsg_get_u32(e, "value", &v)) { - int t = str2val(htsmsg_get_str(e, "comparison") ?: "", - extjs_filtcmp_tab); - idnode_filter_add_num(&conf->filter, k, v, t == -1 ? IC_EQ : t); - } - } else if (!strcmp(t, "boolean")) { - uint32_t v; - if (!htsmsg_get_u32(e, "value", &v)) - idnode_filter_add_bool(&conf->filter, k, v, IC_EQ); - } - } - htsmsg_destroy(t); - } - if ((str = http_arg_get(args, "sort"))) { - conf->sort.key = str; - if ((str = http_arg_get(args, "dir")) && !strcmp(str, "DESC")) - conf->sort.dir = IS_DSC; - else - conf->sort.dir = IS_ASC; - } else - conf->sort.key = NULL; -} - -static void -extjs_idnode_grid - (idnode_set_t *ins, extjs_grid_conf_t *conf, htsmsg_t *out) -{ - int i; - htsmsg_t *list = htsmsg_create_list(); - if (conf->sort.key) - idnode_set_sort(ins, &conf->sort); - for (i = conf->start; i < ins->is_count && conf->limit != 0; i++) { - htsmsg_t *e = htsmsg_create_map(); - htsmsg_add_str(e, "uuid", idnode_uuid_as_str(ins->is_array[i])); - idnode_read0(ins->is_array[i], e, 0); - htsmsg_add_msg(list, NULL, e); - if (conf->limit > 0) conf->limit--; - } - pthread_mutex_unlock(&global_lock); - free(ins->is_array); - idnode_filter_clear(&conf->filter); - htsmsg_add_msg(out, "entries", list); - htsmsg_add_u32(out, "total", ins->is_count); -} - -static int -extjs_mpegts_service - (http_connection_t *hc, const char *remain, void *opaque) -{ - //char buf[256]; - mpegts_network_t *mn; - mpegts_mux_t *mm; - mpegts_service_t *ms; - htsbuf_queue_t *hq = &hc->hc_reply; - const char *op = http_arg_get(&hc->hc_req_args, "op"); - htsmsg_t *out = htsmsg_create_map(); - extjs_grid_conf_t conf = { 0 }; - - if (!strcmp(op, "list")) { - idnode_set_t ins = { 0 }; - extjs_grid_conf(&hc->hc_req_args, &conf); - pthread_mutex_lock(&global_lock); - LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) { - LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link) { - LIST_FOREACH(ms, &mm->mm_services, s_dvb_mux_link) { - idnode_set_add(&ins, (idnode_t*)ms, &conf.filter); - } - } - } - extjs_idnode_grid(&ins, &conf, out); - } else if (!strcmp(op, "class")) { - htsmsg_t *list = idclass_serialize(&mpegts_service_class); - htsmsg_add_msg(out, "entries", list); - } - - htsmsg_json_serialize(out, hq, 0); - http_output_content(hc, "text/x-json; charset=UTF-8"); - htsmsg_destroy(out); - - return 0; -} - -static void -http_api_boilerplate - (http_connection_t *hc, const char **op, htsmsg_t **args) -{ - const char *s; - *op = http_arg_get(&hc->hc_req_args, "op"); - s = http_arg_get(&hc->hc_req_args, "args"); - if (s) - *args = htsmsg_json_deserialize(s); - else - *args = NULL; - if (!*op && *args) - *op = htsmsg_get_str(*args, "method"); // HTSP compat -} - -static int -extjs_mpegts_mux - (http_connection_t *hc, const char *remain, void *opaque) -{ - mpegts_network_t *mn; - mpegts_mux_t *mm; - htsbuf_queue_t *hq = &hc->hc_reply; - const char *op; - htsmsg_t *args; - htsmsg_t *out = htsmsg_create_map(); - extjs_grid_conf_t conf = { 0 }; - - http_api_boilerplate(hc, &op, &args); - if (!op) - return HTTP_STATUS_BAD_REQUEST; - - if (!strcmp(op, "list")) { - idnode_set_t ins = { 0 }; - extjs_grid_conf(&hc->hc_req_args, &conf); - pthread_mutex_lock(&global_lock); - LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) { - LIST_FOREACH(mm, &mn->mn_muxes, mm_network_link) { - idnode_set_add(&ins, (idnode_t*)mm, &conf.filter); - } - } - extjs_idnode_grid(&ins, &conf, out); - } else if (!strcmp(op, "class")) { - htsmsg_t *list = idclass_serialize(&mpegts_mux_class); - htsmsg_add_msg(out, "entries", list); - } else if (!strcmp(op, "delete") && args) { - htsmsg_field_t *f; - htsmsg_t *uuids = htsmsg_get_list(args, "uuids"); - if (uuids) { - pthread_mutex_lock(&global_lock); - HTSMSG_FOREACH(f, uuids) { - if (f->hmf_type == HMF_STR) - mpegts_mux_delete_by_uuid(f->hmf_str); - } - pthread_mutex_unlock(&global_lock); - } - } - - htsmsg_json_serialize(out, hq, 0); - http_output_content(hc, "text/x-json; charset=UTF-8"); - htsmsg_destroy(out); - - return 0; -} - -static int -extjs_linuxdvb_satconf - (http_connection_t *hc, const char *remain, void *opaque) -{ - struct linuxdvb_satconf; - extern const idclass_t linuxdvb_satconf_class; - extern struct linuxdvb_satconf *linuxdvb_satconf_create0(const char*, htsmsg_t*); - mpegts_input_t *mi; - htsbuf_queue_t *hq = &hc->hc_reply; - const char *op = http_arg_get(&hc->hc_req_args, "op"); - htsmsg_t *out = htsmsg_create_map(); - extjs_grid_conf_t conf = { 0 }; - - if (!strcmp(op, "list")) { - idnode_set_t ins = { 0 }; - extjs_grid_conf(&hc->hc_req_args, &conf); - pthread_mutex_lock(&global_lock); - - LIST_FOREACH(mi, &mpegts_input_all, mi_global_link) - if (idnode_is_instance((idnode_t*)mi, &linuxdvb_satconf_class)) - idnode_set_add(&ins, (idnode_t*)mi, &conf.filter); - extjs_idnode_grid(&ins, &conf, out); - } else if (!strcmp(op, "class")) { - htsmsg_t *list = idclass_serialize(&linuxdvb_satconf_class); - htsmsg_add_msg(out, "entries", list); - } else if (!strcmp(op, "create")) { - idnode_t *in; - htsmsg_t *conf = NULL; - const char *c; - if ((c = http_arg_get(&hc->hc_req_args, "conf"))) - conf = htsmsg_json_deserialize(c); - if (!conf) - return HTTP_STATUS_BAD_REQUEST; - pthread_mutex_lock(&global_lock); - in = (idnode_t*)linuxdvb_satconf_create0(NULL, conf); - if (in) in->in_class->ic_save(in); - pthread_mutex_unlock(&global_lock); - } - - htsmsg_json_serialize(out, hq, 0); - http_output_content(hc, "text/x-json; charset=UTF-8"); - htsmsg_destroy(out); - - return 0; -} - - -static int -extjs_mpegts_network - (http_connection_t *hc, const char *remain, void *opaque) -{ - mpegts_network_t *mn = NULL; - htsbuf_queue_t *hq = &hc->hc_reply; - const char *op; - htsmsg_t *args; - htsmsg_t *out = htsmsg_create_map(); - extjs_grid_conf_t conf = { 0 }; - - http_api_boilerplate(hc, &op, &args); - if (!op) - return HTTP_STATUS_BAD_REQUEST; - - if (!strcmp(op, "list")) { - idnode_set_t ins = { 0 }; - extjs_grid_conf(&hc->hc_req_args, &conf); - pthread_mutex_lock(&global_lock); - LIST_FOREACH(mn, &mpegts_network_all, mn_global_link) { - idnode_set_add(&ins, (idnode_t*)mn, &conf.filter); - } - extjs_idnode_grid(&ins, &conf, out); - } else if (!strcmp(op, "class")) { - htsmsg_t *c = idclass_serialize(&mpegts_network_class); - htsmsg_add_msg(out, "entries", c); - } else if (!strcmp(op, "class_list")) { - mpegts_network_builder_t *mnb; - htsmsg_t *e, *c = htsmsg_create_list(); - LIST_FOREACH(mnb, &mpegts_network_builders, link) - if ((e = idclass_serialize(mnb->idc))) - htsmsg_add_msg(c, NULL, e); - htsmsg_add_msg(out, "entries", c); - } else if (!strcmp(op, "delete") && args) { - htsmsg_field_t *f; - htsmsg_t *uuids = htsmsg_get_list(args, "uuids"); - if (uuids) { - pthread_mutex_lock(&global_lock); - HTSMSG_FOREACH(f, uuids) { - if (f->hmf_type == HMF_STR) - mpegts_network_delete_by_uuid(f->hmf_str); - } - pthread_mutex_unlock(&global_lock); - } - } else if (!strcmp(op, "create")) { - htsmsg_t *conf = NULL; - const char *s, *c; - if (!(s = http_arg_get(&hc->hc_req_args, "conf"))) - return HTTP_STATUS_BAD_REQUEST; - if (!(c = http_arg_get(&hc->hc_req_args, "class"))) - return HTTP_STATUS_BAD_REQUEST; - if (!(conf = htsmsg_json_deserialize(s))) - return HTTP_STATUS_BAD_REQUEST; - pthread_mutex_lock(&global_lock); - mn = mpegts_network_build(c, conf); - if (mn) mn->mn_config_save(mn); - pthread_mutex_unlock(&global_lock); - } else if (!strcmp(op, "mux_class")) { - const idclass_t *idc; - const char *uuid = http_arg_get(&hc->hc_req_args, "uuid"); - pthread_mutex_lock(&global_lock); - mn = (uuid ? mpegts_network_find(uuid) : NULL); - if (!mn) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_BAD_REQUEST; - } - if (!(idc = mn->mn_mux_class(mn))) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_BAD_REQUEST; - } - htsmsg_t *c = idclass_serialize(idc); - htsmsg_add_msg(out, "entries", c); - pthread_mutex_unlock(&global_lock); - } else if (!strcmp(op, "mux_create")) { - mpegts_mux_t *mm; - htsmsg_t *conf = NULL; - const char *c; - const char *uuid = http_arg_get(&hc->hc_req_args, "uuid"); - pthread_mutex_lock(&global_lock); - mn = (uuid ? mpegts_network_find(uuid) : NULL); - if (!mn) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_BAD_REQUEST; - } - if ((c = http_arg_get(&hc->hc_req_args, "conf"))) - conf = htsmsg_json_deserialize(c); - if (!conf) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_BAD_REQUEST; - } - mm = mn->mn_mux_create2(mn, conf); - if(mm) mm->mm_config_save(mm); - pthread_mutex_unlock(&global_lock); - if (!mm) return HTTP_STATUS_BAD_REQUEST; // TODO: error message - } else { - return HTTP_STATUS_BAD_REQUEST; - } - - htsmsg_json_serialize(out, hq, 0); - http_output_content(hc, "text/x-json; charset=UTF-8"); - htsmsg_destroy(out); - - return 0; -} - -static int -extjs_mpegts_input - (http_connection_t *hc, const char *remain, void *opaque) -{ - mpegts_input_t *mi; - mpegts_network_t *mn; - htsbuf_queue_t *hq = &hc->hc_reply; - const char *op = http_arg_get(&hc->hc_req_args, "op"); - htsmsg_t *out = htsmsg_create_map(); - extjs_grid_conf_t conf = { 0 }; - - if (!op) return 404; - - if (!strcmp(op, "list")) { - idnode_set_t ins = { 0 }; - extjs_grid_conf(&hc->hc_req_args, &conf); - pthread_mutex_lock(&global_lock); - LIST_FOREACH(mi, &mpegts_input_all, mi_global_link) - idnode_set_add(&ins, (idnode_t*)mi, &conf.filter); - extjs_idnode_grid(&ins, &conf, out); - } else if (!strcmp(op, "class")) { - htsmsg_t *list = idclass_serialize(&mpegts_input_class); - htsmsg_add_msg(out, "entries", list); - } else if (!strcmp(op, "network_list")) { - const char *uuid = http_arg_get(&hc->hc_req_args, "uuid"); - if (!uuid) return HTTP_STATUS_BAD_REQUEST; - pthread_mutex_lock(&global_lock); - mi = mpegts_input_find(uuid); - if (mi) { - int i; - idnode_set_t *is = mi->mi_network_list(mi); - if (is) { - htsmsg_t *l = htsmsg_create_list(); - for (i = 0; i < is->is_count; i++) { - char buf[256]; - htsmsg_t *e = htsmsg_create_map(); - mn = (mpegts_network_t*)is->is_array[i]; - htsmsg_add_str(e, "key", idnode_uuid_as_str(is->is_array[i])); - mn->mn_display_name(mn, buf, sizeof(buf)); - htsmsg_add_str(e, "val", buf); - htsmsg_add_msg(l, NULL, e); - } - htsmsg_add_msg(out, "entries", l); - idnode_set_free(is); - } - } - pthread_mutex_unlock(&global_lock); -#if 0 - } else if (!strcmp(op, "network_class")) { - const char *uuid = http_arg_get(&hc->hc_req_args, "uuid"); - if (!uuid) return 404; - pthread_mutex_lock(&global_lock); - mpegts_input_t *mi = idnode_find(uuid, &mpegts_input_class); - if (!mi) { - pthread_mutex_unlock(&global_lock); - return 404; - } - htsmsg_t *list= idclass_serialize(mi->mi_network_class(mi)); - htsmsg_add_msg(out, "entries", list); - pthread_mutex_unlock(&global_lock); - } else if (!strcmp(op, "network_create")) { - const char *uuid = http_arg_get(&hc->hc_req_args, "uuid"); - const char *conf = http_arg_get(&hc->hc_req_args, "conf"); - if (!uuid || !conf) return 404; - pthread_mutex_lock(&global_lock); - mi = idnode_find(uuid, &mpegts_input_class); - if (!mi) { - pthread_mutex_unlock(&global_lock); - return 404; - } - mn = mi->mi_network_create(mi, htsmsg_json_deserialize(conf)); - if (mn) - mn->mn_config_save(mn); - else { - // TODO: Check for error - } - pthread_mutex_unlock(&global_lock); -#endif - } - - htsmsg_json_serialize(out, hq, 0); - http_output_content(hc, "text/x-json; charset=UTF-8"); - htsmsg_destroy(out); - - return 0; -} - -/** - * DVB WEB user interface - */ -void -extjs_start_dvb(void) -{ - http_path_add("/api/mpegts/network", - NULL, extjs_mpegts_network, ACCESS_WEB_INTERFACE); - http_path_add("/api/mpegts/mux", - NULL, extjs_mpegts_mux, ACCESS_WEB_INTERFACE); - http_path_add("/api/mpegts/service", - NULL, extjs_mpegts_service, ACCESS_WEB_INTERFACE); - http_path_add("/api/mpegts/input", - NULL, extjs_mpegts_input, ACCESS_WEB_INTERFACE); - http_path_add("/api/linuxdvb/satconf", - NULL, extjs_linuxdvb_satconf, ACCESS_WEB_INTERFACE); -#if 0 - http_path_add("/dvb/locations", - NULL, extjs_dvblocations, ACCESS_WEB_INTERFACE); - - http_path_add("/dvb/adapter", - NULL, extjs_dvbadapter, ACCESS_ADMIN); - - - http_path_add("/dvb/muxes", - NULL, extjs_dvbmuxes, ACCESS_ADMIN); - - http_path_add("/dvb/services", - NULL, extjs_dvbservices, ACCESS_ADMIN); - - http_path_add("/dvb/lnbtypes", - NULL, extjs_lnbtypes, ACCESS_ADMIN); - - http_path_add("/dvb/satconf", - NULL, extjs_dvbsatconf, ACCESS_ADMIN); - - http_path_add("/dvb/feopts", - NULL, extjs_dvb_feopts, ACCESS_ADMIN); - - http_path_add("/dvb/addmux", - NULL, extjs_dvb_addmux, ACCESS_ADMIN); - http_path_add("/dvb/copymux", - NULL, extjs_dvb_copymux, ACCESS_ADMIN); -#endif - -} diff --git a/src/webui/extjs_v4l.c b/src/webui/extjs_v4l.c deleted file mode 100644 index 10556124..00000000 --- a/src/webui/extjs_v4l.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * tvheadend, EXTJS based interface - * Copyright (C) 2008 Andreas Öman - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "htsmsg.h" -#include "htsmsg_json.h" - -#include "tvheadend.h" -#include "http.h" -#include "webui.h" -#include "access.h" -#include "channels.h" -#include "psi.h" - -#include "v4l.h" -#include "serviceprobe.h" - - - -/** - * - */ -static htsmsg_t * -json_single_record(htsmsg_t *rec, const char *root) -{ - htsmsg_t *out, *array; - - out = htsmsg_create_map(); - array = htsmsg_create_list(); - - htsmsg_add_msg(array, NULL, rec); - htsmsg_add_msg(out, root, array); - return out; -} - - - -/** - * - */ -static int -extjs_v4ladapter(http_connection_t *hc, const char *remain, void *opaque) -{ - htsbuf_queue_t *hq = &hc->hc_reply; - v4l_adapter_t *va; - htsmsg_t *out, *array, *r; - const char *op = http_arg_get(&hc->hc_req_args, "op"); - const char *s; - - pthread_mutex_lock(&global_lock); - - if(remain == NULL) { - /* Just list all adapters */ - - array = htsmsg_create_list(); - - TAILQ_FOREACH(va, &v4l_adapters, va_global_link) - htsmsg_add_msg(array, NULL, v4l_adapter_build_msg(va)); - - pthread_mutex_unlock(&global_lock); - out = htsmsg_create_map(); - htsmsg_add_msg(out, "entries", array); - - htsmsg_json_serialize(out, hq, 0); - htsmsg_destroy(out); - http_output_content(hc, "text/x-json; charset=UTF-8"); - return 0; - } - - if((va = v4l_adapter_find_by_identifier(remain)) == NULL) { - pthread_mutex_unlock(&global_lock); - return 404; - } - - if(!strcmp(op, "load")) { - r = htsmsg_create_map(); - htsmsg_add_str(r, "id", va->va_identifier); - htsmsg_add_str(r, "device", va->va_path ?: "No hardware attached"); - htsmsg_add_str(r, "name", va->va_displayname); - htsmsg_add_u32(r, "logging", va->va_logging); - - out = json_single_record(r, "v4ladapters"); - } else if(!strcmp(op, "save")) { - - if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL) - v4l_adapter_set_displayname(va, s); - - s = http_arg_get(&hc->hc_req_args, "logging"); - v4l_adapter_set_logging(va, !!s); - - out = htsmsg_create_map(); - htsmsg_add_u32(out, "success", 1); - - } else { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_BAD_REQUEST; - } - pthread_mutex_unlock(&global_lock); - - htsmsg_json_serialize(out, hq, 0); - htsmsg_destroy(out); - - http_output_content(hc, "text/x-json; charset=UTF-8"); - return 0; -} - -/** - * - */ -static void -service_update_v4l(htsmsg_t *in) -{ - htsmsg_field_t *f; - htsmsg_t *c; - service_t *t; - uint32_t u32; - const char *id; - int save; - - TAILQ_FOREACH(f, &in->hm_fields, hmf_link) { - if((c = htsmsg_get_map_by_field(f)) == NULL || - (id = htsmsg_get_str(c, "id")) == NULL) - continue; - - if((t = service_find_by_identifier(id)) == NULL) - continue; - - save = 0; - - if(!htsmsg_get_u32(c, "frequency", &u32)) { - t->s_v4l_frequency = u32; - save = 1; - } - if(save) - t->s_config_save(t); // Save config - } -} - - - -/** - * - */ -static htsmsg_t * -build_record_v4l(service_t *t) -{ - htsmsg_t *r = htsmsg_create_map(); - - // htsmsg_add_str(r, "id", t->s_identifier); // XXX(dvbreorg) - - htsmsg_add_str(r, "channelname", t->s_ch ? t->s_ch->ch_name : ""); - htsmsg_add_u32(r, "frequency", t->s_v4l_frequency); - htsmsg_add_u32(r, "enabled", t->s_enabled); - return r; -} - -/** - * - */ -static int -v4l_servicecmp(const void *A, const void *B) -{ - service_t *a = *(service_t **)A; - service_t *b = *(service_t **)B; - - return (int)a->s_v4l_frequency - (int)b->s_v4l_frequency; -} - -/** - * - */ -static int -extjs_v4lservices(http_connection_t *hc, const char *remain, void *opaque) -{ - v4l_adapter_t *va; - htsbuf_queue_t *hq = &hc->hc_reply; - htsmsg_t *out, *in, *array; - const char *op = http_arg_get(&hc->hc_req_args, "op"); - const char *entries = http_arg_get(&hc->hc_req_args, "entries"); - service_t *t, **tvec; - int count = 0, i = 0; - - pthread_mutex_lock(&global_lock); - - if((va = v4l_adapter_find_by_identifier(remain)) == NULL) { - pthread_mutex_unlock(&global_lock); - return 404; - } - - in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL; - - if(!strcmp(op, "get")) { - - LIST_FOREACH(t, &va->va_services, s_group_link) - count++; - tvec = alloca(sizeof(service_t *) * count); - LIST_FOREACH(t, &va->va_services, s_group_link) - tvec[i++] = t; - - out = htsmsg_create_map(); - array = htsmsg_create_list(); - - qsort(tvec, count, sizeof(service_t *), v4l_servicecmp); - - for(i = 0; i < count; i++) - htsmsg_add_msg(array, NULL, build_record_v4l(tvec[i])); - - htsmsg_add_msg(out, "entries", array); - - } else if(!strcmp(op, "update")) { - if(in != NULL) { - extjs_service_update(in); // Generic service parameters - service_update_v4l(in); // V4L speicifc - } - - out = htsmsg_create_map(); - - } else if(!strcmp(op, "create")) { - - out = build_record_v4l(v4l_service_find(va, NULL, 1)); - - } else if(!strcmp(op, "delete")) { - if(in != NULL) - extjs_service_delete(in); - - out = htsmsg_create_map(); - - } else { - pthread_mutex_unlock(&global_lock); - htsmsg_destroy(in); - return HTTP_STATUS_BAD_REQUEST; - } - - htsmsg_destroy(in); - - 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; -} - -#if 0 - -/** - * - */ -void -extjs_list_v4l_adapters(htsmsg_t *array) -{ - v4l_adapter_t *va; - - TAILQ_FOREACH(va, &v4l_adapters, va_global_link) - htsmsg_add_msg(array, NULL, v4l_adapter_build_msg(va)); -} -#endif - - -/** - * WEB user interface - */ -void -extjs_start_v4l(void) -{ - http_path_add("/v4l/adapter", - NULL, extjs_v4ladapter, ACCESS_ADMIN); - - http_path_add("/v4l/services", - NULL, extjs_v4lservices, ACCESS_ADMIN); -} diff --git a/src/webui/static/app/idnode.js b/src/webui/static/app/idnode.js index 4ca791c4..9ccbfee6 100644 --- a/src/webui/static/app/idnode.js +++ b/src/webui/static/app/idnode.js @@ -7,10 +7,10 @@ tvheadend.idnode_get_enum = function ( conf ) { /* Build key */ var key = conf.url; - if (conf.event) - key += conf.event; if (conf.params) key += '?' + Ext.util.JSON.encode(conf.params); + if (conf.event) + key += '+' + conf.event; /* Use cached */ if (key in tvheadend.idnode_enum_stores) @@ -22,6 +22,7 @@ tvheadend.idnode_get_enum = function ( conf ) url : conf.url, baseParams : conf.params || {}, fields : conf.fields || [ 'key', 'val' ], + id : conf.id || 'key', autoLoad : true, }); tvheadend.idnode_enum_stores[key] = st; @@ -206,13 +207,11 @@ tvheadend.idnode_editor = function(item, conf) handler : function() { var node = panel.getForm().getFieldValues(); node.uuid = item.uuid; - var params = { - op : 'save', - nodes : Ext.util.JSON.encode([node]) - }; Ext.Ajax.request({ - url : 'api/idnode', - params : params, + url : 'api/idnode/save', + params : { + args : Ext.encode({node: node}) + }, success : function(d) { } }); @@ -259,10 +258,12 @@ tvheadend.idnode_create = function(conf) params['uuid'] = puuid; if (pclass) params['class'] = pclass - params['conf'] = Ext.util.JSON.encode(panel.getForm().getFieldValues()); + params['conf'] = panel.getForm().getFieldValues(); Ext.Ajax.request({ - url : conf.create.url || conf.url, - params : params, + url : conf.create.url || conf.url + '/create', + params : { + args : Ext.util.JSON.encode(params) + }, success : function(d) { win.close(); } @@ -294,7 +295,7 @@ tvheadend.idnode_create = function(conf) /* Create window */ win = new Ext.Window({ - title : 'Add ' + conf.title, + title : 'Add ' + conf.titleS, layout : 'fit', autoWidth : true, autoHeight : true, @@ -379,7 +380,7 @@ tvheadend.idnode_create = function(conf) win.show(); } else { Ext.Ajax.request({ - url : conf.url, + url : conf.url + '/class', params : conf.params, success : function(d) { d = json_decode(d); @@ -458,15 +459,12 @@ tvheadend.idnode_grid = function(panel, conf) /* Store */ var store = new Ext.data.JsonStore({ root : 'entries', - url : conf.url, + url : conf.url + '/grid', autoLoad : true, id : 'uuid', totalProperty : 'total', fields : fields, - remoteSort : true, - baseParams : { - op : 'list', - } + remoteSort : true }); /* Model */ @@ -507,10 +505,9 @@ tvheadend.idnode_grid = function(panel, conf) out[x].uuid = mr[x].id; } Ext.Ajax.request({ - url : 'api/idnode', + url : 'api/idnode/save', params : { - op : 'save', - nodes : Ext.encode(out) + args : Ext.encode({node : out}) }, success : function(d) { @@ -555,10 +552,9 @@ tvheadend.idnode_grid = function(panel, conf) for ( var i = 0; i < r.length; i++ ) uuids.push(r[i].id) Ext.Ajax.request({ - url : conf.url, + url : 'api/idnode/delete', params : { - op: 'delete', - args : Ext.util.JSON.encode({ uuids: uuids}) + args : Ext.util.JSON.encode({ uuid: uuids}) }, success : function(d) { @@ -580,9 +576,8 @@ tvheadend.idnode_grid = function(panel, conf) if (r) { if (conf.edittree) { var p = tvheadend.idnode_tree({ - url : 'api/idnode', + url : 'api/idnode/tree', params : { - op : 'childs', root : r.id } }); @@ -598,15 +593,14 @@ tvheadend.idnode_grid = function(panel, conf) w.show(); } else { Ext.Ajax.request({ - url : 'api/idnode', + url : 'api/idnode/load', params : { - op: 'get', uuid: r.id }, success : function(d) { d = json_decode(d); - var p = tvheadend.idnode_editor(d, {}); + var p = tvheadend.idnode_editor(d[0], {}); var w = new Ext.Window({ title : 'Edit ' + conf.titleS, layout : 'fit', @@ -678,10 +672,7 @@ tvheadend.idnode_grid = function(panel, conf) /* Request data */ if (!conf.fields) { Ext.Ajax.request({ - url : conf.url, - params : { - op: 'class' - }, + url : conf.url + '/class', success : function(d) { build(json_decode(d).props); @@ -696,7 +687,6 @@ tvheadend.idnode_tree = function (conf) { var current = null; var params = conf.params || {}; - params.op = 'childs'; var loader = new Ext.tree.TreeLoader({ dataUrl : conf.url, baseParams : params, diff --git a/src/webui/static/app/mpegts.js b/src/webui/static/app/mpegts.js index ce39561d..dca6600b 100644 --- a/src/webui/static/app/mpegts.js +++ b/src/webui/static/app/mpegts.js @@ -2,39 +2,47 @@ * DVB network */ -tvheadend.network_classes = new Ext.data.JsonStore({ - autoLoad : true, +tvheadend.network_builders = new Ext.data.JsonStore({ + url : 'api/mpegts/network/builders', root : 'entries', - fields : [ 'class', 'caption', 'props' ], - id : 'class', - url : 'api/mpegts/network', - baseParams : { - op: 'class_list' - } + fields : [ 'class', 'caption', 'props' ], + id : 'class', + autoLoad : true, }); + +tvheadend.network_list = new Ext.data.JsonStore({ + url : 'api/idnode/load', + baseParams : { class : 'mpegts_network', enum: 1 }, + root : 'entries', + fields : [ 'uuid', 'title' ], + id : 'uuid', + autoLoad : true, +}); + tvheadend.comet.on('mpegts_network', function() { - tvheadend.network_classes.reload(); + // TODO: Might be a bit excessive + tvheadend.network_builders.reload(); + tvheadend.network_list.reload(); }); tvheadend.networks = function(panel) { tvheadend.idnode_grid(panel, { - titleS : 'Network', - titleP : 'Networks', url : 'api/mpegts/network', comet : 'mpegts_network', + titleS : 'Network', + titleP : 'Networks', add : { - url : 'api/mpegts/network', - title : 'Network', + titleS : 'Network', select : { - caption : 'Type', - store : tvheadend.network_classes, + label : 'Type', + store : tvheadend.network_builders, displayField : 'caption', valueField : 'class', propField : 'props', }, create : { - params : { op: 'create' } + url : 'api/mpegts/network/create' } }, del : true @@ -44,24 +52,23 @@ tvheadend.networks = function(panel) tvheadend.muxes = function(panel) { tvheadend.idnode_grid(panel, { - titleS : 'Mux', - titleP : 'Muxes', url : 'api/mpegts/mux', comet : 'mpegts_mux', - add : { - title : 'Mux', - url : 'api/mpegts/network', - select : { - caption : 'Network', - params : { op: 'list', limit: -1 }, - displayField : 'networkname', + titleS : 'Mux', + titleP : 'Muxes', + add : { + titleS : 'Mux', + select : { + label : 'Network', + store : tvheadend.network_list, + displayField : 'title', valueField : 'uuid', clazz : { - params : { op: 'mux_class' } + url : 'api/mpegts/network/mux_class' } }, - create : { - params : { op: 'mux_create' } + create : { + url : 'api/mpegts/network/mux_create', } }, del : true @@ -72,9 +79,9 @@ tvheadend.services = function(panel) { tvheadend.idnode_grid(panel, { url : 'api/mpegts/service', + comet : 'service', titleS : 'Service', titleP : 'Services', - comet : 'service', add : false, del : false }); @@ -83,20 +90,11 @@ tvheadend.services = function(panel) tvheadend.satconfs = function(panel) { tvheadend.idnode_grid(panel, { - titleS : 'Satconf', - titleP : 'Satconfs', url : 'api/linuxdvb/satconf', comet : 'linuxdvb_satconf', - add : { - title : 'Satconf', - url : 'api/linuxdvb/satconf', - create : { - params : { op: 'create' } - }, - params : { - op: 'class' - } - }, + titleS : 'Satconf', + titleP : 'Satconfs', + add : {}, del : true, edittree : true, }); diff --git a/src/webui/static/app/tvadapters.js b/src/webui/static/app/tvadapters.js index 010d2e40..c5b4a43d 100644 --- a/src/webui/static/app/tvadapters.js +++ b/src/webui/static/app/tvadapters.js @@ -1,4 +1,4 @@ tvheadend.tvadapters = function() { // return tvheadend.item_browser('tvadapters', 'TV Adapters'); - return tvheadend.idnode_tree({ url: 'tvadapters', title: 'TV adapters'}); + return tvheadend.idnode_tree({ url: 'api/tvadapter/tree', title: 'TV adapters'}); } diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 55e36641..a3280f2a 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -267,7 +267,9 @@ function accessUpdate(o) { tvheadend.capabilities.indexOf('v4l') != -1) { tabs2.push(new tvheadend.tvadapters); } +/* tabs2.push(new tvheadend.iptv); +*/ tvheadend.conf_dvbin = new Ext.TabPanel({ activeTab: 0, autoScroll: true, diff --git a/src/webui/webui.c b/src/webui/webui.c index faae50ba..31248e81 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -974,5 +974,6 @@ webui_init(void) simpleui_start(); extjs_start(); comet_init(); + webui_api_init(); } diff --git a/src/webui/webui.h b/src/webui/webui.h index bf4efcda..409f745e 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -46,6 +46,8 @@ void extjs_service_update(htsmsg_t *in); void extjs_service_delete(htsmsg_t *in); +void webui_api_init ( void ); + /** * diff --git a/src/webui/webui_api.c b/src/webui/webui_api.c new file mode 100644 index 00000000..45b93dea --- /dev/null +++ b/src/webui/webui_api.c @@ -0,0 +1,93 @@ +/* + * tvheadend, WebAPI access point + * + * 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 . + */ + +#include "tvheadend.h" +#include "webui.h" +#include "access.h" +#include "http.h" +#include "api.h" +#include "htsmsg.h" +#include "htsmsg_json.h" + +static int +webui_api_handler + ( http_connection_t *hc, const char *remain, void *opaque ) +{ + int r; + http_arg_t *ha; + htsmsg_t *args, *resp = NULL; + const char *a = http_arg_get(&hc->hc_req_args, "args"); + const char *op = http_arg_get(&hc->hc_req_args, "method"); + + // Compat + if (!op) + op = http_arg_get(&hc->hc_req_args, "op"); + + /* Parse arguments */ + if (a) + args = htsmsg_json_deserialize(a); + else + args = htsmsg_create_map(); + if (!args) + return HTTP_STATUS_BAD_REQUEST; + + /* Add HTTP arguments?? */ + TAILQ_FOREACH(ha, &hc->hc_req_args, link) { + // Ignore obvious keys + if (strcmp("op", ha->key) && + strcmp("method", ha->key) && + strcmp("args", ha->key)) + htsmsg_add_str(args, ha->key, ha->val); + } + + /* Add operation */ + if (!htsmsg_get_str(args, "method") && op) + htsmsg_add_str(args, "method", op); + + /* Call */ + r = api_exec(remain, args, &resp); + + /* Convert error */ + if (r) { + switch (r) { + case EACCES: + r = HTTP_STATUS_UNAUTHORIZED; + case ENOENT: + case ENOSYS: + r = HTTP_STATUS_NOT_FOUND; + default: + r = HTTP_STATUS_BAD_REQUEST; + } + } + + /* Output response */ + if (resp) { + htsmsg_json_serialize(resp, &hc->hc_reply, 0); + http_output_content(hc, "text/x-json; charset=UTF-8"); + htsmsg_destroy(resp); + } + + return r; +} + +void +webui_api_init ( void ) +{ + http_path_add("/api", NULL, webui_api_handler, ACCESS_WEB_INTERFACE); +}