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