api webui: migrated most of the new web API stuff to the new API subsystem

This commit is contained in:
Adam Sutton 2013-08-15 12:05:04 +01:00
parent c368e32f0d
commit dee72beaa5
9 changed files with 662 additions and 1060 deletions

View file

@ -112,6 +112,7 @@ SRCS = src/version.c \
SRCS += \
src/api.c \
src/api/api_idnode.c \
src/api/api_mpegts.c \
SRCS += \
src/parsers/parsers.c \
@ -136,7 +137,6 @@ 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\
@ -208,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

View file

@ -116,4 +116,5 @@ void api_init ( void )
/* Subsystems */
api_idnode_init();
api_mpegts_init();
}

View file

@ -55,14 +55,33 @@ int api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp );
/*
* Initialise
*/
void api_init ( void );
void api_init ( void );
void api_idnode_init ( void );
void api_mpegts_init ( void );
/*
* Re-usable functions
* IDnode
*/
int api_idnode_tree0
( const char *uuid, const char *root, idnode_set_t *(*rootfn)(void),
htsmsg_t **resp );
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__ */

View file

@ -26,18 +26,288 @@
#include "htsmsg.h"
#include "api.h"
int
api_idnode_tree0
( const char *uuid, const char *root,
idnode_set_t *(*rootfn)(void), htsmsg_t **resp )
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;
/* Validate */
if (!uuid)
/* UUID */
if (!(uuid = htsmsg_get_str(args, "uuid")))
return EINVAL;
isroot = !strcmp("root", uuid);
/* Root UUID */
if (!rootfn)
root = htsmsg_get_str(args, "root");
/* Is root? */
isroot = (strcmp("root", uuid) == 0);
if (isroot && !(root || rootfn))
return EINVAL;
@ -76,66 +346,46 @@ api_idnode_tree0
return 0;
}
static int
int
api_idnode_class
( const char *class, htsmsg_t *args, htsmsg_t **resp )
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
int i, brief;
int err = EINVAL;
const char *name;
const idclass_t *idc;
idnode_set_t *is;
idnode_t *in;
htsmsg_t *e;
// TODO: this only works if pass as integer
brief = htsmsg_get_bool_or_default(args, "brief", 0);
pthread_mutex_lock(&global_lock);
/* Find class */
if (!(idc = idclass_find(class))) {
pthread_mutex_unlock(&global_lock);
return EINVAL;
/* Lookup */
if (!opaque) {
if (!(name = htsmsg_get_str(args, "name")))
goto exit;
if (!(idc = idclass_find(name)))
goto exit;
} else {
idc = opaque;
}
*resp = 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 (brief) {
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(*resp, NULL, e);
}
}
err = 0;
*resp = idclass_serialize(idc);
exit:
pthread_mutex_unlock(&global_lock);
return 0;
return err;
}
static int
api_idnode_load
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, *class;
const char *uuid;
/* Class based */
if ((class = htsmsg_get_str(args, "class")))
return api_idnode_class(class, args, resp);
/* ID based */
if (!(f = htsmsg_field_find(args, "uuid")))
return EINVAL;
@ -146,54 +396,38 @@ api_idnode_load
if (f->hmf_type == HMF_STR) {
uuid = htsmsg_field_get_string(f);
in = idnode_find(uuid, NULL);
if (in)
*resp = idnode_serialize(in);
else
if (in) {
idnode_delete(in);
} else
err = ENOENT;
/* Multiple */
} else if (f->hmf_type == HMF_LIST) {
l = htsmsg_get_list_by_field(f);
*resp = htsmsg_create_list();
HTSMSG_FOREACH(f, l) {
if (!(uuid = htsmsg_field_get_string(f))) continue;
if (!(in = idnode_find(uuid, NULL))) continue;
htsmsg_add_msg(*resp, NULL, idnode_serialize(in));
idnode_delete(in);
}
/* Invalid */
} else {
err = EINVAL;
}
// TODO: should we return the UUIDs that are deleted?
if (!err)
*resp = htsmsg_create_map();
pthread_mutex_unlock(&global_lock);
return err;
}
static int
api_idnode_save
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
return 0;
}
static int
api_idnode_tree
( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
const char *uuid = htsmsg_get_str(args, "uuid");
const char *root = htsmsg_get_str(args, "root");
return api_idnode_tree0(uuid, root, NULL, resp);
}
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/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 },
};

324
src/api/api_mpegts.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View file

@ -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/idnode2", 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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <arpa/inet.h>
#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
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <arpa/inet.h>
#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);
}

View file

@ -31,7 +31,7 @@ webui_api_handler
{
int r;
http_arg_t *ha;
htsmsg_t *args, *resp;
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");
@ -74,9 +74,10 @@ webui_api_handler
default:
r = HTTP_STATUS_BAD_REQUEST;
}
}
/* Output response */
} else {
if (resp) {
htsmsg_json_serialize(resp, &hc->hc_reply, 0);
http_output_content(hc, "text/x-json; charset=UTF-8");
htsmsg_destroy(resp);