#include "channels.h"
#include "psi.h"
-#include "dvb/dvb.h"
-#include "dvb/dvb_support.h"
-#include "dvb/dvb_preconf.h"
#include "dvr/dvr.h"
-#include "v4l.h"
#include "transports.h"
#include "serviceprobe.h"
#include "xmltv.h"
extjs_load(hq, "static/app/cwceditor.js");
extjs_load(hq, "static/app/capmteditor.js");
extjs_load(hq, "static/app/tvadapters.js");
+#if ENABLE_LINUXDVB
extjs_load(hq, "static/app/dvb.js");
+#endif
extjs_load(hq, "static/app/iptv.js");
+#if ENABLE_V4L
extjs_load(hq, "static/app/v4l.js");
+#endif
extjs_load(hq, "static/app/chconf.js");
extjs_load(hq, "static/app/epg.js");
extjs_load(hq, "static/app/dvr.js");
return 0;
}
-
-/**
- *
- */
-static int
-extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- const char *s = http_arg_get(&hc->hc_req_args, "node");
- const char *a = http_arg_get(&hc->hc_req_args, "adapter");
- th_dvb_adapter_t *tda;
- htsmsg_t *out = NULL;
-
- if(s == NULL || a == NULL)
- return HTTP_STATUS_BAD_REQUEST;
-
- pthread_mutex_lock(&global_lock);
-
- if(http_access_verify(hc, ACCESS_ADMIN)) {
- pthread_mutex_unlock(&global_lock);
- return HTTP_STATUS_UNAUTHORIZED;
- }
-
- if((tda = dvb_adapter_find_by_identifier(a)) == NULL) {
- pthread_mutex_unlock(&global_lock);
- return HTTP_STATUS_BAD_REQUEST;
- }
-
- pthread_mutex_unlock(&global_lock);
-
- if((out = dvb_mux_preconf_get_node(tda->tda_type, s)) == NULL)
- return 404;
-
- htsmsg_json_serialize(out, hq, 0);
- htsmsg_destroy(out);
- http_output_content(hc, "text/x-json; charset=UTF-8");
- return 0;
-}
-
/**
*
*/
}
+
/**
*
*/
-static int
-extjs_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
+void
+extjs_transport_delete(htsmsg_t *in)
{
- htsbuf_queue_t *hq = &hc->hc_reply;
- th_dvb_adapter_t *tda, *ref;
- htsmsg_t *out, *array, *r;
- const char *op = http_arg_get(&hc->hc_req_args, "op");
- const char *sibling = http_arg_get(&hc->hc_req_args, "sibling");
- const char *s, *sc;
- th_dvb_mux_instance_t *tdmi;
+ htsmsg_field_t *f;
th_transport_t *t;
+ const char *id;
- pthread_mutex_lock(&global_lock);
-
- if(remain == NULL) {
- /* Just list all adapters */
-
- ref = sibling != NULL ? dvb_adapter_find_by_identifier(sibling) : NULL;
-
- array = htsmsg_create_list();
-
- TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
- if(ref == NULL || (ref != tda && ref->tda_type == tda->tda_type))
- htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda));
- }
- 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((tda = dvb_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", tda->tda_identifier);
- htsmsg_add_str(r, "device", tda->tda_rootpath ?: "No hardware attached");
- htsmsg_add_str(r, "name", tda->tda_displayname);
- htsmsg_add_u32(r, "automux", tda->tda_autodiscovery);
- htsmsg_add_u32(r, "idlescan", tda->tda_idlescan);
- htsmsg_add_u32(r, "qmon", tda->tda_qmon);
- htsmsg_add_u32(r, "nitoid", tda->tda_nitoid);
- htsmsg_add_str(r, "diseqcversion",
- ((const char *[]){"DiSEqC 1.0 / 2.0",
- "DiSEqC 1.1 / 2.1"})
- [tda->tda_diseqc_version % 2]);
-
- out = json_single_record(r, "dvbadapters");
- } else if(!strcmp(op, "save")) {
-
- if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL)
- dvb_adapter_set_displayname(tda, s);
-
- s = http_arg_get(&hc->hc_req_args, "automux");
- dvb_adapter_set_auto_discovery(tda, !!s);
-
- s = http_arg_get(&hc->hc_req_args, "idlescan");
- dvb_adapter_set_idlescan(tda, !!s);
-
- s = http_arg_get(&hc->hc_req_args, "qmon");
- dvb_adapter_set_qmon(tda, !!s);
-
- if((s = http_arg_get(&hc->hc_req_args, "nitoid")) != NULL)
- dvb_adapter_set_nitoid(tda, atoi(s));
-
- if((s = http_arg_get(&hc->hc_req_args, "diseqcversion")) != NULL) {
- if(!strcmp(s, "DiSEqC 1.0 / 2.0"))
- dvb_adapter_set_diseqc_version(tda, 0);
- else if(!strcmp(s, "DiSEqC 1.1 / 2.1"))
- dvb_adapter_set_diseqc_version(tda, 1);
- }
-
- out = htsmsg_create_map();
- htsmsg_add_u32(out, "success", 1);
- } else if(!strcmp(op, "addnetwork")) {
-
- sc = http_arg_get(&hc->hc_req_args, "satconf");
-
- if((s = http_arg_get(&hc->hc_req_args, "network")) != NULL)
- dvb_mux_preconf_add_network(tda, s, sc);
-
- out = htsmsg_create_map();
- htsmsg_add_u32(out, "success", 1);
-
- } else if(!strcmp(op, "serviceprobe")) {
-
- tvhlog(LOG_NOTICE, "web interface",
- "Service probe started on \"%s\"", tda->tda_displayname);
-
- LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
- LIST_FOREACH(t, &tdmi->tdmi_transports, tht_group_link) {
- if(t->tht_enabled)
- serviceprobe_enqueue(t);
- }
- }
-
-
- out = htsmsg_create_map();
- htsmsg_add_u32(out, "success", 1);
-
- } else {
- pthread_mutex_unlock(&global_lock);
- return HTTP_STATUS_BAD_REQUEST;
+ TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
+ if((id = htsmsg_field_get_string(f)) != NULL &&
+ (t = transport_find_by_identifier(id)) != NULL)
+ transport_destroy(t);
}
- 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
-mux_update(htsmsg_t *in)
+transport_update(htsmsg_t *in)
{
htsmsg_field_t *f;
htsmsg_t *c;
- th_dvb_mux_instance_t *tdmi;
+ th_transport_t *t;
uint32_t u32;
const char *id;
+ const char *chname;
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((tdmi = dvb_mux_find_by_identifier(id)) == NULL)
+ if((t = transport_find_by_identifier(id)) == NULL)
continue;
if(!htsmsg_get_u32(c, "enabled", &u32))
- dvb_mux_set_enable(tdmi, u32);
- }
-}
-
-
-/**
- *
- */
-static void
-mux_delete(htsmsg_t *in)
-{
- htsmsg_field_t *f;
- th_dvb_mux_instance_t *tdmi;
- const char *id;
+ transport_set_enable(t, u32);
- TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
- if((id = htsmsg_field_get_string(f)) != NULL &&
- (tdmi = dvb_mux_find_by_identifier(id)) != NULL)
- dvb_mux_destroy(tdmi);
+ if((chname = htsmsg_get_str(c, "channelname")) != NULL)
+ transport_map_channel(t, channel_find_by_name(chname, 1), 1);
}
}
*
*/
static int
-extjs_dvbmuxes(http_connection_t *hc, const char *remain, void *opaque)
+extjs_servicedetails(http_connection_t *hc,
+ const char *remain, void *opaque)
{
htsbuf_queue_t *hq = &hc->hc_reply;
- th_dvb_adapter_t *tda;
- htsmsg_t *out, *array, *in;
- const char *op = http_arg_get(&hc->hc_req_args, "op");
- const char *entries = http_arg_get(&hc->hc_req_args, "entries");
- th_dvb_mux_instance_t *tdmi;
+ htsmsg_t *out, *streams, *c;
+ th_transport_t *t;
+ th_stream_t *st;
+ char buf[40];
pthread_mutex_lock(&global_lock);
- if(remain == NULL ||
- (tda = dvb_adapter_find_by_identifier(remain)) == NULL) {
+ if(remain == NULL || (t = transport_find_by_identifier(remain)) == NULL) {
pthread_mutex_unlock(&global_lock);
return 404;
}
- in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
+ streams = htsmsg_create_list();
- out = htsmsg_create_map();
+ LIST_FOREACH(st, &t->tht_components, st_link) {
+ c = htsmsg_create_map();
- if(!strcmp(op, "get")) {
- array = htsmsg_create_list();
+ htsmsg_add_u32(c, "pid", st->st_pid);
- LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link)
- htsmsg_add_msg(array, NULL, dvb_mux_build_msg(tdmi));
+ htsmsg_add_str(c, "type", streaming_component_type2txt(st->st_type));
- htsmsg_add_msg(out, "entries", array);
- } else if(!strcmp(op, "update")) {
- if(in != NULL)
- mux_update(in);
+ switch(st->st_type) {
+ default:
+ htsmsg_add_str(c, "details", "");
+ break;
- out = htsmsg_create_map();
+ case SCT_CA:
+ snprintf(buf, sizeof(buf), "%s (0x%04x)",
+ psi_caid2name(st->st_caid), st->st_caid);
+ htsmsg_add_str(c, "details", buf);
+ break;
- } else if(!strcmp(op, "delete")) {
- if(in != NULL)
- mux_delete(in);
-
- out = htsmsg_create_map();
+ case SCT_AC3:
+ case SCT_AAC:
+ case SCT_MPEG2AUDIO:
+ htsmsg_add_str(c, "details", st->st_lang);
+ break;
- } else {
- pthread_mutex_unlock(&global_lock);
- if(in != NULL)
- htsmsg_destroy(in);
- htsmsg_destroy(out);
- return HTTP_STATUS_BAD_REQUEST;
+ case SCT_DVBSUB:
+ snprintf(buf, sizeof(buf), "%s (%04x %04x)",
+ st->st_lang, st->st_composition_id, st->st_ancillary_id);
+ htsmsg_add_str(c, "details", buf);
+ break;
+
+ case SCT_MPEG2VIDEO:
+ case SCT_H264:
+ buf[0] = 0;
+ if(st->st_frame_duration)
+ snprintf(buf, sizeof(buf), "%2.2f Hz",
+ 90000.0 / st->st_frame_duration);
+ htsmsg_add_str(c, "details", buf);
+ break;
+ }
+
+ htsmsg_add_msg(streams, NULL, c);
}
+ out = htsmsg_create_map();
+ htsmsg_add_str(out, "title", t->tht_svcname ?: "unnamed transport");
+
+ htsmsg_add_msg(out, "streams", streams);
+
pthread_mutex_unlock(&global_lock);
-
+
htsmsg_json_serialize(out, hq, 0);
htsmsg_destroy(out);
http_output_content(hc, "text/x-json; charset=UTF-8");
-
- if(in != NULL)
- htsmsg_destroy(in);
-
return 0;
}
+
/**
*
*/
-static void
-transport_delete(htsmsg_t *in)
+static int
+extjs_mergechannel(http_connection_t *hc, const char *remain, void *opaque)
{
- htsmsg_field_t *f;
- th_transport_t *t;
- const char *id;
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ const char *target = http_arg_get(&hc->hc_req_args, "targetID");
+ htsmsg_t *out;
+ channel_t *src, *dst;
- TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
- if((id = htsmsg_field_get_string(f)) != NULL &&
- (t = transport_find_by_identifier(id)) != NULL)
- transport_destroy(t);
+ if(remain == NULL || target == NULL)
+ return 400;
+
+ pthread_mutex_lock(&global_lock);
+
+ src = channel_find_by_identifier(atoi(remain));
+ dst = channel_find_by_identifier(atoi(target));
+
+ if(src == NULL || dst == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return 404;
+ }
+
+ out = htsmsg_create_map();
+
+ if(src != dst) {
+ channel_merge(dst, src);
+ htsmsg_add_u32(out, "success", 1);
+ } else {
+
+ htsmsg_add_u32(out, "success", 0);
+ htsmsg_add_str(out, "msg", "Target same as source");
}
+
+ pthread_mutex_unlock(&global_lock);
+
+ htsmsg_json_serialize(out, hq, 0);
+ htsmsg_destroy(out);
+ http_output_content(hc, "text/x-json; charset=UTF-8");
+ return 0;
}
+
/**
*
*/
static void
-transport_update(htsmsg_t *in)
+transport_update_iptv(htsmsg_t *in)
{
htsmsg_field_t *f;
htsmsg_t *c;
th_transport_t *t;
uint32_t u32;
- const char *id;
- const char *chname;
+ const char *id, *s;
+ int save;
TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
if((c = htsmsg_get_map_by_field(f)) == NULL ||
if((t = transport_find_by_identifier(id)) == NULL)
continue;
- if(!htsmsg_get_u32(c, "enabled", &u32))
- transport_set_enable(t, u32);
+ save = 0;
- if((chname = htsmsg_get_str(c, "channelname")) != NULL)
- transport_map_channel(t, channel_find_by_name(chname, 1), 1);
+ if(!htsmsg_get_u32(c, "port", &u32)) {
+ t->tht_iptv_port = u32;
+ save = 1;
+ }
+
+ if((s = htsmsg_get_str(c, "group")) != NULL) {
+ inet_pton(AF_INET, s, &t->tht_iptv_group.s_addr);
+ save = 1;
+ }
+
+
+ save |= tvh_str_update(&t->tht_iptv_iface, htsmsg_get_str(c, "interface"));
+ if(save)
+ t->tht_config_save(t); // Save config
}
}
-/**
- *
- */
-static int
-transportcmp(const void *A, const void *B)
-{
- th_transport_t *a = *(th_transport_t **)A;
- th_transport_t *b = *(th_transport_t **)B;
-
- return strcasecmp(a->tht_svcname ?: "\0377", b->tht_svcname ?: "\0377");
-}
-
-/**
- *
- */
-static int
-extjs_dvbservices(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- th_dvb_adapter_t *tda;
- htsmsg_t *out, *array, *in;
- const char *op = http_arg_get(&hc->hc_req_args, "op");
- const char *entries = http_arg_get(&hc->hc_req_args, "entries");
- th_dvb_mux_instance_t *tdmi;
- th_transport_t *t, **tvec;
- int count = 0, i = 0;
-
- pthread_mutex_lock(&global_lock);
-
- if(remain == NULL ||
- (tda = dvb_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")) {
-
- out = htsmsg_create_map();
- array = htsmsg_create_list();
-
- LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
- LIST_FOREACH(t, &tdmi->tdmi_transports, tht_group_link) {
- count++;
- }
- }
-
- tvec = alloca(sizeof(th_transport_t *) * count);
-
- LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
- LIST_FOREACH(t, &tdmi->tdmi_transports, tht_group_link) {
- tvec[i++] = t;
- }
- }
-
- qsort(tvec, count, sizeof(th_transport_t *), transportcmp);
-
- for(i = 0; i < count; i++)
- htsmsg_add_msg(array, NULL, dvb_transport_build_msg(tvec[i]));
-
- htsmsg_add_msg(out, "entries", array);
-
- } else if(!strcmp(op, "update")) {
- if(in != NULL)
- transport_update(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;
-}
-
-/**
- *
- */
-static int
-extjs_lnbtypes(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- htsmsg_t *out;
-
- out = htsmsg_create_map();
-
- htsmsg_add_msg(out, "entries", dvb_lnblist_get());
-
- htsmsg_json_serialize(out, hq, 0);
- htsmsg_destroy(out);
- http_output_content(hc, "text/x-json; charset=UTF-8");
- return 0;
-}
-
-
-
-
-/**
- *
- */
-static int
-extjs_dvbsatconf(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- th_dvb_adapter_t *tda;
- htsmsg_t *out;
-
- pthread_mutex_lock(&global_lock);
-
- if(remain == NULL ||
- (tda = dvb_adapter_find_by_identifier(remain)) == NULL) {
- pthread_mutex_unlock(&global_lock);
- return 404;
- }
-
- out = htsmsg_create_map();
- htsmsg_add_msg(out, "entries", dvb_satconf_list(tda));
-
- 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 int
-extjs_servicedetails(http_connection_t *hc,
- const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- htsmsg_t *out, *streams, *c;
- th_transport_t *t;
- th_stream_t *st;
- char buf[40];
-
- pthread_mutex_lock(&global_lock);
-
- if(remain == NULL || (t = transport_find_by_identifier(remain)) == NULL) {
- pthread_mutex_unlock(&global_lock);
- return 404;
- }
-
- streams = htsmsg_create_list();
-
- LIST_FOREACH(st, &t->tht_components, st_link) {
- c = htsmsg_create_map();
-
- htsmsg_add_u32(c, "pid", st->st_pid);
-
- htsmsg_add_str(c, "type", streaming_component_type2txt(st->st_type));
-
- switch(st->st_type) {
- default:
- htsmsg_add_str(c, "details", "");
- break;
-
- case SCT_CA:
- snprintf(buf, sizeof(buf), "%s (0x%04x)",
- psi_caid2name(st->st_caid), st->st_caid);
- htsmsg_add_str(c, "details", buf);
- break;
-
- case SCT_AC3:
- case SCT_AAC:
- case SCT_MPEG2AUDIO:
- htsmsg_add_str(c, "details", st->st_lang);
- break;
-
- case SCT_DVBSUB:
- snprintf(buf, sizeof(buf), "%s (%04x %04x)",
- st->st_lang, st->st_composition_id, st->st_ancillary_id);
- htsmsg_add_str(c, "details", buf);
- break;
-
- case SCT_MPEG2VIDEO:
- case SCT_H264:
- buf[0] = 0;
- if(st->st_frame_duration)
- snprintf(buf, sizeof(buf), "%2.2f Hz",
- 90000.0 / st->st_frame_duration);
- htsmsg_add_str(c, "details", buf);
- break;
- }
-
- htsmsg_add_msg(streams, NULL, c);
- }
-
- out = htsmsg_create_map();
- htsmsg_add_str(out, "title", t->tht_svcname ?: "unnamed transport");
-
- htsmsg_add_msg(out, "streams", streams);
-
- 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 int
-extjs_dvb_feopts(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- char *a, *r;
- th_dvb_adapter_t *tda;
- htsmsg_t *e, *out;
-
- if(remain == NULL)
- return 400;
-
- r = tvh_strdupa(remain);
- if((a = strchr(r, '/')) == NULL)
- return 400;
- *a++ = 0;
-
- pthread_mutex_lock(&global_lock);
-
- if((tda = dvb_adapter_find_by_identifier(a)) == NULL) {
- pthread_mutex_unlock(&global_lock);
- return 404;
- }
-
- e = dvb_fe_opts(tda, r);
-
- if(e == NULL) {
- pthread_mutex_unlock(&global_lock);
- return 400;
- }
-
- out = htsmsg_create_map();
- htsmsg_add_msg(out, "entries", e);
-
- 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_dvb_addmux(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsmsg_t *out;
- htsbuf_queue_t *hq = &hc->hc_reply;
- struct http_arg_list *args = &hc->hc_req_args;
- th_dvb_adapter_t *tda;
- const char *err;
- pthread_mutex_lock(&global_lock);
-
- if(remain == NULL ||
- (tda = dvb_adapter_find_by_identifier(remain)) == NULL) {
- pthread_mutex_unlock(&global_lock);
- return 404;
- }
-
- err =
- dvb_mux_add_by_params(tda,
- atoi(http_arg_get(args, "frequency")?:"-1"),
- atoi(http_arg_get(args, "symbolrate")?: "-1"),
- atoi(http_arg_get(args, "bandwidthID")?: "-1"),
- atoi(http_arg_get(args, "constellationID")?: "-1"),
- atoi(http_arg_get(args, "delsysID")?: "-1"),
- atoi(http_arg_get(args, "tmodeID")?: "-1"),
- atoi(http_arg_get(args, "guardintervalID")?: "-1"),
- atoi(http_arg_get(args, "hierarchyID")?: "-1"),
- atoi(http_arg_get(args, "fechiID")?: "-1"),
- atoi(http_arg_get(args, "fecloID")?: "-1"),
- atoi(http_arg_get(args, "fecID")?: "-1"),
- atoi(http_arg_get(args, "polarisationID")?: "-1"),
- http_arg_get(args, "satconfID") ?: NULL);
-
- if(err != NULL)
- tvhlog(LOG_ERR, "web interface",
- "Unable to create mux on %s: %s",
- tda->tda_displayname, err);
-
- pthread_mutex_unlock(&global_lock);
-
- out = htsmsg_create_map();
- htsmsg_json_serialize(out, hq, 0);
- http_output_content(hc, "text/x-json; charset=UTF-8");
- htsmsg_destroy(out);
-
- return 0;
-}
-
-
-/**
- *
- */
-static int
-extjs_dvb_copymux(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsmsg_t *out;
- htsbuf_queue_t *hq = &hc->hc_reply;
- th_dvb_adapter_t *tda;
- htsmsg_t *in;
- const char *entries = http_arg_get(&hc->hc_req_args, "entries");
- const char *id;
- htsmsg_field_t *f;
- th_dvb_mux_instance_t *tdmi;
-
- in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
-
- if(in == NULL)
- return 400;
-
- pthread_mutex_lock(&global_lock);
-
- if(remain == NULL ||
- (tda = dvb_adapter_find_by_identifier(remain)) == NULL) {
- pthread_mutex_unlock(&global_lock);
- return 404;
- }
-
-
- TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
- if((id = htsmsg_field_get_string(f)) != NULL &&
- (tdmi = dvb_mux_find_by_identifier(id)) != NULL &&
- tda != tdmi->tdmi_adapter) {
-
- if(dvb_mux_copy(tda, tdmi)) {
- char buf[100];
- dvb_mux_nicename(buf, sizeof(buf), tdmi);
-
- tvhlog(LOG_NOTICE, "DVB",
- "Skipped copy of mux %s to adapter %s, mux already exist",
- buf, tda->tda_displayname);
- }
- }
- }
-
- pthread_mutex_unlock(&global_lock);
-
- out = htsmsg_create_map();
- htsmsg_json_serialize(out, hq, 0);
- http_output_content(hc, "text/x-json; charset=UTF-8");
- htsmsg_destroy(out);
-
- return 0;
-}
-
-
-/**
- *
- */
-static int
-extjs_mergechannel(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- const char *target = http_arg_get(&hc->hc_req_args, "targetID");
- htsmsg_t *out;
- channel_t *src, *dst;
-
- if(remain == NULL || target == NULL)
- return 400;
-
- pthread_mutex_lock(&global_lock);
-
- src = channel_find_by_identifier(atoi(remain));
- dst = channel_find_by_identifier(atoi(target));
-
- if(src == NULL || dst == NULL) {
- pthread_mutex_unlock(&global_lock);
- return 404;
- }
-
- out = htsmsg_create_map();
-
- if(src != dst) {
- channel_merge(dst, src);
- htsmsg_add_u32(out, "success", 1);
- } else {
-
- htsmsg_add_u32(out, "success", 0);
- htsmsg_add_str(out, "msg", "Target same as source");
- }
-
- pthread_mutex_unlock(&global_lock);
-
- htsmsg_json_serialize(out, hq, 0);
- htsmsg_destroy(out);
- http_output_content(hc, "text/x-json; charset=UTF-8");
- return 0;
-}
-
-
-
-/**
- *
- */
-static void
-transport_update_iptv(htsmsg_t *in)
-{
- htsmsg_field_t *f;
- htsmsg_t *c;
- th_transport_t *t;
- uint32_t u32;
- const char *id, *s;
- 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 = transport_find_by_identifier(id)) == NULL)
- continue;
-
- save = 0;
-
- if(!htsmsg_get_u32(c, "port", &u32)) {
- t->tht_iptv_port = u32;
- save = 1;
- }
-
- if((s = htsmsg_get_str(c, "group")) != NULL) {
- inet_pton(AF_INET, s, &t->tht_iptv_group.s_addr);
- save = 1;
- }
-
-
- save |= tvh_str_update(&t->tht_iptv_iface, htsmsg_get_str(c, "interface"));
- if(save)
- t->tht_config_save(t); // Save config
- }
-}
-
-
-
+
/**
*
*/
} else if(!strcmp(op, "delete")) {
if(in != NULL)
- transport_delete(in);
+ extjs_transport_delete(in);
out = htsmsg_create_map();
return 0;
}
-/**
- *
- */
-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
-transport_update_v4l(htsmsg_t *in)
+void
+extjs_transport_update(htsmsg_t *in)
{
htsmsg_field_t *f;
htsmsg_t *c;
th_transport_t *t;
uint32_t u32;
const char *id;
- int save;
+ const char *chname;
TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
if((c = htsmsg_get_map_by_field(f)) == NULL ||
if((t = transport_find_by_identifier(id)) == NULL)
continue;
- save = 0;
-
- if(!htsmsg_get_u32(c, "frequency", &u32)) {
- t->tht_v4l_frequency = u32;
- save = 1;
- }
- if(save)
- t->tht_config_save(t); // Save config
- }
-}
-
-
-
-/**
- *
- */
-static htsmsg_t *
-build_record_v4l(th_transport_t *t)
-{
- htsmsg_t *r = htsmsg_create_map();
-
- htsmsg_add_str(r, "id", t->tht_identifier);
-
- htsmsg_add_str(r, "channelname", t->tht_ch ? t->tht_ch->ch_name : "");
- htsmsg_add_u32(r, "frequency", t->tht_v4l_frequency);
- htsmsg_add_u32(r, "enabled", t->tht_enabled);
- return r;
-}
-
-/**
- *
- */
-static int
-v4l_transportcmp(const void *A, const void *B)
-{
- th_transport_t *a = *(th_transport_t **)A;
- th_transport_t *b = *(th_transport_t **)B;
-
- return (int)a->tht_v4l_frequency - (int)b->tht_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");
- th_transport_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_transports, tht_group_link)
- count++;
- tvec = alloca(sizeof(th_transport_t *) * count);
- LIST_FOREACH(t, &va->va_transports, tht_group_link)
- tvec[i++] = t;
-
- out = htsmsg_create_map();
- array = htsmsg_create_list();
-
- qsort(tvec, count, sizeof(th_transport_t *), v4l_transportcmp);
-
- 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) {
- transport_update(in); // Generic transport parameters
- transport_update_v4l(in); // V4L speicifc
- }
-
- out = htsmsg_create_map();
-
- } else if(!strcmp(op, "create")) {
-
- out = build_record_v4l(v4l_transport_find(va, NULL, 1));
-
- } else if(!strcmp(op, "delete")) {
- if(in != NULL)
- transport_delete(in);
-
- out = htsmsg_create_map();
+ if(!htsmsg_get_u32(c, "enabled", &u32))
+ transport_set_enable(t, u32);
- } else {
- pthread_mutex_unlock(&global_lock);
- htsmsg_destroy(in);
- return HTTP_STATUS_BAD_REQUEST;
+ if((chname = htsmsg_get_str(c, "channelname")) != NULL)
+ transport_map_channel(t, channel_find_by_name(chname, 1), 1);
}
-
- 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;
}
{
htsbuf_queue_t *hq = &hc->hc_reply;
htsmsg_t *out, *array;
- th_dvb_adapter_t *tda;
- v4l_adapter_t *va;
pthread_mutex_lock(&global_lock);
/* Just list all adapters */
array = htsmsg_create_list();
- TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
- htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda));
+#if ENABLE_LINUXDVB
+ extjs_list_dvb_adapters(array);
+#endif
- TAILQ_FOREACH(va, &v4l_adapters, va_global_link)
- htsmsg_add_msg(array, NULL, v4l_adapter_build_msg(va));
+#if ENABLE_V4L
+ extjs_list_v4l_adapters(array);
+#endif
pthread_mutex_unlock(&global_lock);
out = htsmsg_create_map();
http_path_add("/about.html", NULL, page_about, ACCESS_WEB_INTERFACE);
http_path_add("/extjs.html", NULL, extjs_root, ACCESS_WEB_INTERFACE);
http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE);
- http_path_add("/dvbnetworks", NULL, extjs_dvbnetworks, ACCESS_WEB_INTERFACE);
http_path_add("/channels", NULL, extjs_channels, ACCESS_WEB_INTERFACE);
http_path_add("/xmltv", NULL, extjs_xmltv, ACCESS_WEB_INTERFACE);
http_path_add("/channeltags", NULL, extjs_channeltags, ACCESS_WEB_INTERFACE);
http_path_add("/mergechannel",
NULL, extjs_mergechannel, ACCESS_ADMIN);
- 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);
-
http_path_add("/iptv/services",
NULL, extjs_iptvservices, ACCESS_ADMIN);
http_path_add("/servicedetails",
NULL, extjs_servicedetails, ACCESS_ADMIN);
-
- http_path_add("/v4l/adapter",
- NULL, extjs_v4ladapter, ACCESS_ADMIN);
-
- http_path_add("/v4l/services",
- NULL, extjs_v4lservices, ACCESS_ADMIN);
-
http_path_add("/tv/adapter",
NULL, extjs_tvadapter, ACCESS_ADMIN);
+#if ENABLE_LINUXDVB
+ extjs_start_dvb();
+#endif
+
+#if ENABLE_V4L
+ extjs_start_v4l();
+#endif
}
--- /dev/null
+/*
+ * 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 <libavutil/avstring.h>
+
+#include "htsmsg.h"
+#include "htsmsg_json.h"
+
+#include "tvhead.h"
+#include "http.h"
+#include "webui.h"
+#include "access.h"
+#include "dtable.h"
+#include "channels.h"
+#include "psi.h"
+#include "transports.h"
+#include "serviceprobe.h"
+
+#include "dvb/dvb.h"
+#include "dvb/dvb_support.h"
+#include "dvb/dvb_preconf.h"
+#include "dvr/dvr.h"
+
+/**
+ *
+ */
+static int
+extjs_dvbnetworks(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ const char *s = http_arg_get(&hc->hc_req_args, "node");
+ const char *a = http_arg_get(&hc->hc_req_args, "adapter");
+ th_dvb_adapter_t *tda;
+ htsmsg_t *out = NULL;
+
+ if(s == NULL || a == NULL)
+ return HTTP_STATUS_BAD_REQUEST;
+
+ pthread_mutex_lock(&global_lock);
+
+ if(http_access_verify(hc, ACCESS_ADMIN)) {
+ pthread_mutex_unlock(&global_lock);
+ return HTTP_STATUS_UNAUTHORIZED;
+ }
+
+ if((tda = dvb_adapter_find_by_identifier(a)) == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return HTTP_STATUS_BAD_REQUEST;
+ }
+
+ pthread_mutex_unlock(&global_lock);
+
+ if((out = dvb_mux_preconf_get_node(tda->tda_type, s)) == NULL)
+ return 404;
+
+ htsmsg_json_serialize(out, hq, 0);
+ htsmsg_destroy(out);
+ http_output_content(hc, "text/x-json; charset=UTF-8");
+ return 0;
+}
+
+/**
+ *
+ */
+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_dvbadapter(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ th_dvb_adapter_t *tda, *ref;
+ htsmsg_t *out, *array, *r;
+ const char *op = http_arg_get(&hc->hc_req_args, "op");
+ const char *sibling = http_arg_get(&hc->hc_req_args, "sibling");
+ const char *s, *sc;
+ th_dvb_mux_instance_t *tdmi;
+ th_transport_t *t;
+
+ pthread_mutex_lock(&global_lock);
+
+ if(remain == NULL) {
+ /* Just list all adapters */
+
+ ref = sibling != NULL ? dvb_adapter_find_by_identifier(sibling) : NULL;
+
+ array = htsmsg_create_list();
+
+ TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link) {
+ if(ref == NULL || (ref != tda && ref->tda_type == tda->tda_type))
+ htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda));
+ }
+ 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((tda = dvb_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", tda->tda_identifier);
+ htsmsg_add_str(r, "device", tda->tda_rootpath ?: "No hardware attached");
+ htsmsg_add_str(r, "name", tda->tda_displayname);
+ htsmsg_add_u32(r, "automux", tda->tda_autodiscovery);
+ htsmsg_add_u32(r, "idlescan", tda->tda_idlescan);
+ htsmsg_add_u32(r, "qmon", tda->tda_qmon);
+ htsmsg_add_u32(r, "nitoid", tda->tda_nitoid);
+ htsmsg_add_str(r, "diseqcversion",
+ ((const char *[]){"DiSEqC 1.0 / 2.0",
+ "DiSEqC 1.1 / 2.1"})
+ [tda->tda_diseqc_version % 2]);
+
+ out = json_single_record(r, "dvbadapters");
+ } else if(!strcmp(op, "save")) {
+
+ if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL)
+ dvb_adapter_set_displayname(tda, s);
+
+ s = http_arg_get(&hc->hc_req_args, "automux");
+ dvb_adapter_set_auto_discovery(tda, !!s);
+
+ s = http_arg_get(&hc->hc_req_args, "idlescan");
+ dvb_adapter_set_idlescan(tda, !!s);
+
+ s = http_arg_get(&hc->hc_req_args, "qmon");
+ dvb_adapter_set_qmon(tda, !!s);
+
+ if((s = http_arg_get(&hc->hc_req_args, "nitoid")) != NULL)
+ dvb_adapter_set_nitoid(tda, atoi(s));
+
+ if((s = http_arg_get(&hc->hc_req_args, "diseqcversion")) != NULL) {
+ if(!strcmp(s, "DiSEqC 1.0 / 2.0"))
+ dvb_adapter_set_diseqc_version(tda, 0);
+ else if(!strcmp(s, "DiSEqC 1.1 / 2.1"))
+ dvb_adapter_set_diseqc_version(tda, 1);
+ }
+
+ out = htsmsg_create_map();
+ htsmsg_add_u32(out, "success", 1);
+ } else if(!strcmp(op, "addnetwork")) {
+
+ sc = http_arg_get(&hc->hc_req_args, "satconf");
+
+ if((s = http_arg_get(&hc->hc_req_args, "network")) != NULL)
+ dvb_mux_preconf_add_network(tda, s, sc);
+
+ out = htsmsg_create_map();
+ htsmsg_add_u32(out, "success", 1);
+
+ } else if(!strcmp(op, "serviceprobe")) {
+
+ tvhlog(LOG_NOTICE, "web interface",
+ "Service probe started on \"%s\"", tda->tda_displayname);
+
+ LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
+ LIST_FOREACH(t, &tdmi->tdmi_transports, tht_group_link) {
+ if(t->tht_enabled)
+ serviceprobe_enqueue(t);
+ }
+ }
+
+
+ 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
+mux_update(htsmsg_t *in)
+{
+ htsmsg_field_t *f;
+ htsmsg_t *c;
+ th_dvb_mux_instance_t *tdmi;
+ uint32_t u32;
+ const char *id;
+
+ 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((tdmi = dvb_mux_find_by_identifier(id)) == NULL)
+ continue;
+
+ if(!htsmsg_get_u32(c, "enabled", &u32))
+ dvb_mux_set_enable(tdmi, u32);
+ }
+}
+
+
+/**
+ *
+ */
+static void
+mux_delete(htsmsg_t *in)
+{
+ htsmsg_field_t *f;
+ th_dvb_mux_instance_t *tdmi;
+ const char *id;
+
+ TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
+ if((id = htsmsg_field_get_string(f)) != NULL &&
+ (tdmi = dvb_mux_find_by_identifier(id)) != NULL)
+ dvb_mux_destroy(tdmi);
+ }
+}
+
+
+/**
+ *
+ */
+static int
+extjs_dvbmuxes(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ th_dvb_adapter_t *tda;
+ htsmsg_t *out, *array, *in;
+ const char *op = http_arg_get(&hc->hc_req_args, "op");
+ const char *entries = http_arg_get(&hc->hc_req_args, "entries");
+ th_dvb_mux_instance_t *tdmi;
+
+ pthread_mutex_lock(&global_lock);
+
+ if(remain == NULL ||
+ (tda = dvb_adapter_find_by_identifier(remain)) == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return 404;
+ }
+
+ in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
+
+ out = htsmsg_create_map();
+
+ if(!strcmp(op, "get")) {
+ array = htsmsg_create_list();
+
+ LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link)
+ htsmsg_add_msg(array, NULL, dvb_mux_build_msg(tdmi));
+
+ htsmsg_add_msg(out, "entries", array);
+ } else if(!strcmp(op, "update")) {
+ if(in != NULL)
+ mux_update(in);
+
+ out = htsmsg_create_map();
+
+ } else if(!strcmp(op, "delete")) {
+ if(in != NULL)
+ mux_delete(in);
+
+ out = htsmsg_create_map();
+
+ } else {
+ pthread_mutex_unlock(&global_lock);
+ if(in != NULL)
+ htsmsg_destroy(in);
+ htsmsg_destroy(out);
+ 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");
+
+ if(in != NULL)
+ htsmsg_destroy(in);
+
+ return 0;
+}
+
+
+
+
+/**
+ *
+ */
+static int
+transportcmp(const void *A, const void *B)
+{
+ th_transport_t *a = *(th_transport_t **)A;
+ th_transport_t *b = *(th_transport_t **)B;
+
+ return strcasecmp(a->tht_svcname ?: "\0377", b->tht_svcname ?: "\0377");
+}
+
+/**
+ *
+ */
+static int
+extjs_dvbservices(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ th_dvb_adapter_t *tda;
+ htsmsg_t *out, *array, *in;
+ const char *op = http_arg_get(&hc->hc_req_args, "op");
+ const char *entries = http_arg_get(&hc->hc_req_args, "entries");
+ th_dvb_mux_instance_t *tdmi;
+ th_transport_t *t, **tvec;
+ int count = 0, i = 0;
+
+ pthread_mutex_lock(&global_lock);
+
+ if(remain == NULL ||
+ (tda = dvb_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")) {
+
+ out = htsmsg_create_map();
+ array = htsmsg_create_list();
+
+ LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
+ LIST_FOREACH(t, &tdmi->tdmi_transports, tht_group_link) {
+ count++;
+ }
+ }
+
+ tvec = alloca(sizeof(th_transport_t *) * count);
+
+ LIST_FOREACH(tdmi, &tda->tda_muxes, tdmi_adapter_link) {
+ LIST_FOREACH(t, &tdmi->tdmi_transports, tht_group_link) {
+ tvec[i++] = t;
+ }
+ }
+
+ qsort(tvec, count, sizeof(th_transport_t *), transportcmp);
+
+ for(i = 0; i < count; i++)
+ htsmsg_add_msg(array, NULL, dvb_transport_build_msg(tvec[i]));
+
+ htsmsg_add_msg(out, "entries", array);
+
+ } else if(!strcmp(op, "update")) {
+ if(in != NULL)
+ extjs_transport_update(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;
+}
+
+/**
+ *
+ */
+static int
+extjs_lnbtypes(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ htsmsg_t *out;
+
+ out = htsmsg_create_map();
+
+ htsmsg_add_msg(out, "entries", dvb_lnblist_get());
+
+ htsmsg_json_serialize(out, hq, 0);
+ htsmsg_destroy(out);
+ http_output_content(hc, "text/x-json; charset=UTF-8");
+ return 0;
+}
+
+
+
+
+/**
+ *
+ */
+static int
+extjs_dvbsatconf(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ th_dvb_adapter_t *tda;
+ htsmsg_t *out;
+
+ pthread_mutex_lock(&global_lock);
+
+ if(remain == NULL ||
+ (tda = dvb_adapter_find_by_identifier(remain)) == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return 404;
+ }
+
+ out = htsmsg_create_map();
+ htsmsg_add_msg(out, "entries", dvb_satconf_list(tda));
+
+ 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 int
+extjs_dvb_feopts(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ char *a, *r;
+ th_dvb_adapter_t *tda;
+ htsmsg_t *e, *out;
+
+ if(remain == NULL)
+ return 400;
+
+ r = tvh_strdupa(remain);
+ if((a = strchr(r, '/')) == NULL)
+ return 400;
+ *a++ = 0;
+
+ pthread_mutex_lock(&global_lock);
+
+ if((tda = dvb_adapter_find_by_identifier(a)) == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return 404;
+ }
+
+ e = dvb_fe_opts(tda, r);
+
+ if(e == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return 400;
+ }
+
+ out = htsmsg_create_map();
+ htsmsg_add_msg(out, "entries", e);
+
+ 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_dvb_addmux(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsmsg_t *out;
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ struct http_arg_list *args = &hc->hc_req_args;
+ th_dvb_adapter_t *tda;
+ const char *err;
+ pthread_mutex_lock(&global_lock);
+
+ if(remain == NULL ||
+ (tda = dvb_adapter_find_by_identifier(remain)) == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return 404;
+ }
+
+ err =
+ dvb_mux_add_by_params(tda,
+ atoi(http_arg_get(args, "frequency")?:"-1"),
+ atoi(http_arg_get(args, "symbolrate")?: "-1"),
+ atoi(http_arg_get(args, "bandwidthID")?: "-1"),
+ atoi(http_arg_get(args, "constellationID")?: "-1"),
+ atoi(http_arg_get(args, "delsysID")?: "-1"),
+ atoi(http_arg_get(args, "tmodeID")?: "-1"),
+ atoi(http_arg_get(args, "guardintervalID")?: "-1"),
+ atoi(http_arg_get(args, "hierarchyID")?: "-1"),
+ atoi(http_arg_get(args, "fechiID")?: "-1"),
+ atoi(http_arg_get(args, "fecloID")?: "-1"),
+ atoi(http_arg_get(args, "fecID")?: "-1"),
+ atoi(http_arg_get(args, "polarisationID")?: "-1"),
+ http_arg_get(args, "satconfID") ?: NULL);
+
+ if(err != NULL)
+ tvhlog(LOG_ERR, "web interface",
+ "Unable to create mux on %s: %s",
+ tda->tda_displayname, err);
+
+ pthread_mutex_unlock(&global_lock);
+
+ out = htsmsg_create_map();
+ htsmsg_json_serialize(out, hq, 0);
+ http_output_content(hc, "text/x-json; charset=UTF-8");
+ htsmsg_destroy(out);
+
+ return 0;
+}
+
+
+/**
+ *
+ */
+static int
+extjs_dvb_copymux(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsmsg_t *out;
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ th_dvb_adapter_t *tda;
+ htsmsg_t *in;
+ const char *entries = http_arg_get(&hc->hc_req_args, "entries");
+ const char *id;
+ htsmsg_field_t *f;
+ th_dvb_mux_instance_t *tdmi;
+
+ in = entries != NULL ? htsmsg_json_deserialize(entries) : NULL;
+
+ if(in == NULL)
+ return 400;
+
+ pthread_mutex_lock(&global_lock);
+
+ if(remain == NULL ||
+ (tda = dvb_adapter_find_by_identifier(remain)) == NULL) {
+ pthread_mutex_unlock(&global_lock);
+ return 404;
+ }
+
+
+ TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
+ if((id = htsmsg_field_get_string(f)) != NULL &&
+ (tdmi = dvb_mux_find_by_identifier(id)) != NULL &&
+ tda != tdmi->tdmi_adapter) {
+
+ if(dvb_mux_copy(tda, tdmi)) {
+ char buf[100];
+ dvb_mux_nicename(buf, sizeof(buf), tdmi);
+
+ tvhlog(LOG_NOTICE, "DVB",
+ "Skipped copy of mux %s to adapter %s, mux already exist",
+ buf, tda->tda_displayname);
+ }
+ }
+ }
+
+ pthread_mutex_unlock(&global_lock);
+
+ out = htsmsg_create_map();
+ htsmsg_json_serialize(out, hq, 0);
+ http_output_content(hc, "text/x-json; charset=UTF-8");
+ htsmsg_destroy(out);
+
+ return 0;
+}
+
+/**
+ *
+ */
+void
+extjs_list_dvb_adapters(htsmsg_t *array)
+{
+ th_dvb_adapter_t *tda;
+ TAILQ_FOREACH(tda, &dvb_adapters, tda_global_link)
+ htsmsg_add_msg(array, NULL, dvb_adapter_build_msg(tda));
+}
+
+
+/**
+ * DVB WEB user interface
+ */
+void
+extjs_start_dvb(void)
+{
+ http_path_add("/dvbnetworks",
+ NULL, extjs_dvbnetworks, 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);
+
+}
--- /dev/null
+/*
+ * 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 <libavutil/avstring.h>
+
+#include "htsmsg.h"
+#include "htsmsg_json.h"
+
+#include "tvhead.h"
+#include "http.h"
+#include "webui.h"
+#include "access.h"
+#include "channels.h"
+#include "psi.h"
+
+#include "v4l.h"
+#include "transports.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
+transport_update_v4l(htsmsg_t *in)
+{
+ htsmsg_field_t *f;
+ htsmsg_t *c;
+ th_transport_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 = transport_find_by_identifier(id)) == NULL)
+ continue;
+
+ save = 0;
+
+ if(!htsmsg_get_u32(c, "frequency", &u32)) {
+ t->tht_v4l_frequency = u32;
+ save = 1;
+ }
+ if(save)
+ t->tht_config_save(t); // Save config
+ }
+}
+
+
+
+/**
+ *
+ */
+static htsmsg_t *
+build_record_v4l(th_transport_t *t)
+{
+ htsmsg_t *r = htsmsg_create_map();
+
+ htsmsg_add_str(r, "id", t->tht_identifier);
+
+ htsmsg_add_str(r, "channelname", t->tht_ch ? t->tht_ch->ch_name : "");
+ htsmsg_add_u32(r, "frequency", t->tht_v4l_frequency);
+ htsmsg_add_u32(r, "enabled", t->tht_enabled);
+ return r;
+}
+
+/**
+ *
+ */
+static int
+v4l_transportcmp(const void *A, const void *B)
+{
+ th_transport_t *a = *(th_transport_t **)A;
+ th_transport_t *b = *(th_transport_t **)B;
+
+ return (int)a->tht_v4l_frequency - (int)b->tht_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");
+ th_transport_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_transports, tht_group_link)
+ count++;
+ tvec = alloca(sizeof(th_transport_t *) * count);
+ LIST_FOREACH(t, &va->va_transports, tht_group_link)
+ tvec[i++] = t;
+
+ out = htsmsg_create_map();
+ array = htsmsg_create_list();
+
+ qsort(tvec, count, sizeof(th_transport_t *), v4l_transportcmp);
+
+ 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_transport_update(in); // Generic transport parameters
+ transport_update_v4l(in); // V4L speicifc
+ }
+
+ out = htsmsg_create_map();
+
+ } else if(!strcmp(op, "create")) {
+
+ out = build_record_v4l(v4l_transport_find(va, NULL, 1));
+
+ } else if(!strcmp(op, "delete")) {
+ if(in != NULL)
+ extjs_transport_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;
+}
+
+
+/**
+ *
+ */
+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));
+}
+
+
+/**
+ * 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);
+}