htsclient.c is now no longer supported.
SRCS += avgen.c file_input.c
-SRCS += htsclient.c rtsp.c rtp.c
+SRCS += rtsp.c rtp.c
SRCS += v4l.c
event_t *e;
channel_t *ch;
- RB_FOREACH(ch, &channel_tree, ch_global_link) {
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
e = ch->ch_epg_cur_event;
if(e == NULL)
continue;
contentgroup ?
epg_content_group_find_by_name(contentgroup) : NULL,
- channel ? channel_find(channel, 1) : NULL,
+ channel ? channel_find_by_name(channel, 1) : NULL,
id, creator);
if(id > ar_id_ceil)
if(avcodec_find_encoder(CODEC_ID_MP2) == NULL)
return;
- ch = channel_find("Test PAL", 1);
+ ch = channel_find_by_name("Test PAL", 1);
t = calloc(1, sizeof(th_transport_t));
#include "pvr.h"
#include "autorec.h"
-struct channel_tree channel_tree;
+struct channel_tree channel_name_tree;
+static struct channel_tree channel_identifier_tree;
static int
dictcmp(const char *a, const char *b)
}
+/**
+ *
+ */
+static int
+chidcmp(const channel_t *a, const channel_t *b)
+{
+ return a->ch_id - b->ch_id;
+}
+
+
/**
*
*/
free((void *)n2);
- x = RB_INSERT_SORTED(&channel_tree, ch, ch_global_link, channelcmp);
+ x = RB_INSERT_SORTED(&channel_name_tree, ch, ch_name_link, channelcmp);
assert(x == NULL);
}
+
+
/**
*
*/
-channel_t *
-channel_find(const char *name, int create)
+static channel_t *
+channel_create(const char *name)
{
- channel_t *ch, skel;
-
- skel.ch_name = name;
-
- if((ch = RB_FIND(&channel_tree, &skel, ch_global_link, channelcmp)) != NULL)
- return ch;
-
- if(create == 0)
- return NULL;
+ channel_t *ch, *x;
+ int id;
+
+ ch = RB_LAST(&channel_identifier_tree);
+ if(ch == NULL) {
+ id = 1;
+ } else {
+ id = ch->ch_id + 1;
+ }
ch = calloc(1, sizeof(channel_t));
- ch->ch_index = channel_tree.entries;
-
TAILQ_INIT(&ch->ch_epg_events);
channel_set_name(ch, name);
- ch->ch_tag = tag_get();
+ ch->ch_id = id;
+ x = RB_INSERT_SORTED(&channel_identifier_tree, ch,
+ ch_identifier_link, chidcmp);
+ assert(x == NULL);
return ch;
}
-
-static struct strtab commercial_detect_tab[] = {
- { "none", COMMERCIAL_DETECT_NONE },
- { "ttp192", COMMERCIAL_DETECT_TTP192 },
-};
-
-
/**
*
*/
-void
-channels_load(void)
+channel_t *
+channel_find_by_name(const char *name, int create)
{
-#if 0
- struct config_head cl;
- config_entry_t *ce;
- char buf[PATH_MAX];
- DIR *dir;
- struct dirent *d;
- const char *name, *grp, *x;
- channel_t *ch;
- int v;
-
- TAILQ_INIT(&all_channel_groups);
- TAILQ_INIT(&cl);
-
- snprintf(buf, sizeof(buf), "%s/channel-group-settings.cfg", settings_dir);
- config_read_file0(buf, &cl);
-
- TAILQ_FOREACH(ce, &cl, ce_link) {
- if(ce->ce_type != CFG_SUB || strcasecmp("channel-group", ce->ce_key))
- continue;
-
- if((name = config_get_str_sub(&ce->ce_sub, "name", NULL)) == NULL)
- continue;
-
- channel_group_find(name, 1);
- }
- config_free0(&cl);
-
- tcg = channel_group_find("-disabled-", 1);
- tcg->tcg_cant_delete_me = 1;
- tcg->tcg_hidden = 1;
-
- defgroup = channel_group_find("Uncategorized", 1);
- defgroup->tcg_cant_delete_me = 1;
-
- snprintf(buf, sizeof(buf), "%s/channels", settings_dir);
-
- if((dir = opendir(buf)) == NULL)
- return;
-
- while((d = readdir(dir)) != NULL) {
-
- if(d->d_name[0] == '.')
- continue;
-
- snprintf(buf, sizeof(buf), "%s/channels/%s", settings_dir, d->d_name);
- TAILQ_INIT(&cl);
- config_read_file0(buf, &cl);
-
- name = config_get_str_sub(&cl, "name", NULL);
- grp = config_get_str_sub(&cl, "channel-group", NULL);
- if(name != NULL && grp != NULL) {
- tcg = channel_group_find(grp, 1);
- ch = channel_find(name, 1, tcg);
-
- x = config_get_str_sub(&cl, "commercial-detect", NULL);
- if(x != NULL) {
- v = str2val(x, commercial_detect_tab);
- if(v > 1)
- ch->ch_commercial_detection = v;
- }
-
- if((x = config_get_str_sub(&cl, "icon", NULL)) != NULL)
- ch->ch_icon = strdup(x);
- }
- config_free0(&cl);
- }
-
- closedir(dir);
-
-
- /* Static services */
-
- TAILQ_FOREACH(ce, &config_list, ce_link) {
- if(ce->ce_type == CFG_SUB && !strcasecmp("service", ce->ce_key)) {
- service_load(&ce->ce_sub);
- }
- }
-#endif
+ channel_t skel, *ch;
+ skel.ch_name = (char *)name;
+ ch = RB_FIND(&channel_name_tree, &skel, ch_name_link, channelcmp);
+ if(ch != NULL || create == 0)
+ return ch;
+ return channel_create(name);
}
-
/**
- * The index stuff should go away
+ *
*/
channel_t *
-channel_by_index(uint32_t index)
+channel_find_by_identifier(int id)
{
- channel_t *ch;
+ channel_t skel, *ch;
+ skel.ch_id = id;
+ ch = RB_FIND(&channel_identifier_tree, &skel, ch_identifier_link, chidcmp);
+ return ch;
+}
+
- RB_FOREACH(ch, &channel_tree, ch_global_link)
- if(ch->ch_index == index)
- return ch;
- return NULL;
-}
+static struct strtab commercial_detect_tab[] = {
+ { "none", COMMERCIAL_DETECT_NONE },
+ { "ttp192", COMMERCIAL_DETECT_TTP192 },
+};
/**
*
*/
-channel_t *
-channel_by_tag(uint32_t tag)
+void
+channels_load(void)
{
- channel_t *ch;
-
- RB_FOREACH(ch, &channel_tree, ch_global_link)
- if(ch->ch_tag == tag)
- return ch;
- return NULL;
}
-
/**
* Write out a config file for a channel
*/
channel_save(channel_t *ch)
{
htsmsg_t *m = htsmsg_create();
- htsmsg_add_str(m, "icon", ch->ch_icon);
+ if(ch->ch_icon != NULL)
+ htsmsg_add_str(m, "icon", ch->ch_icon);
+
htsmsg_add_str(m, "commercial_detect",
val2str(ch->ch_commercial_detection,
commercial_detect_tab) ?: "?");
{
th_transport_t *t;
- if(channel_find(newname, 0))
+ if(channel_find_by_name(newname, 0))
return -1;
+ tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" renamed to \"%s\"",
+ ch->ch_name, newname);
+
hts_settings_remove("channels/%s", ch->ch_name);
- RB_REMOVE(&channel_tree, ch, ch_global_link);
+ RB_REMOVE(&channel_name_tree, ch, ch_name_link);
channel_set_name(ch, newname);
LIST_FOREACH(t, &ch->ch_transports, tht_ch_link) {
th_transport_t *t;
th_subscription_t *s;
+ tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" deleted",
+ ch->ch_name);
+
pvr_destroy_by_channel(ch);
while((t = LIST_FIRST(&ch->ch_transports)) != NULL) {
hts_settings_remove("channels/%s", ch->ch_name);
- free((void *)ch->ch_name);
- free((void *)ch->ch_sname);
+ RB_REMOVE(&channel_name_tree, ch, ch_name_link);
+ RB_REMOVE(&channel_identifier_tree, ch, ch_identifier_link);
+
+ free(ch->ch_name);
+ free(ch->ch_sname);
free(ch->ch_icon);
- RB_REMOVE(&channel_tree, ch, ch_global_link);
free(ch);
}
channel_merge(channel_t *dst, channel_t *src)
{
th_transport_t *t;
+
+ tvhlog(LOG_NOTICE, "channels", "Channel \"%s\" merged into \"%s\"",
+ src->ch_name, dst->ch_name);
while((t = LIST_FIRST(&src->ch_transports)) != NULL) {
transport_unmap_channel(t);
void channels_load(void);
-channel_t *channel_by_index(uint32_t id);
+channel_t *channel_find_by_name(const char *name, int create);
-channel_t *channel_by_tag(uint32_t tag);
-
-int id_by_channel(channel_t *ch);
-
-int channel_get_channels(void);
+channel_t *channel_find_by_identifier(int id);
void channel_unsubscribe(th_subscription_t *s);
-channel_t *channel_find(const char *name, int create);
-
void channel_set_teletext_rundown(channel_t *ch, int v);
void channel_settings_write(channel_t *ch);
* Generate a descriptive name for the source
*/
static const char *
-dvb_transport_name(th_transport_t *t)
+dvb_transport_sourcename(th_transport_t *t)
{
- th_dvb_mux_instance_t *tdmi;
- static char buf[200];
+ th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
+
+ return tdmi->tdmi_adapter->tda_displayname;
+}
- tdmi = t->tht_dvb_mux_instance;
- snprintf(buf, sizeof(buf), "\"%s\" on \"%s\"",
- tdmi->tdmi_network ?: "Unknown network",
- tdmi->tdmi_adapter->tda_rootpath);
+/**
+ * Generate a descriptive name for the source
+ */
+static const char *
+dvb_transport_networkname(th_transport_t *t)
+{
+ th_dvb_mux_instance_t *tdmi = t->tht_dvb_mux_instance;
- return buf;
+ return tdmi->tdmi_network;
}
t->tht_start_feed = dvb_start_feed;
t->tht_stop_feed = dvb_stop_feed;
t->tht_config_change = dvb_transport_save;
- t->tht_sourcename = dvb_transport_name;
+ t->tht_sourcename = dvb_transport_sourcename;
+ t->tht_networkname = dvb_transport_networkname;
t->tht_dvb_mux_instance = tdmi;
t->tht_quality_index = dvb_transport_quality;
#include "channels.h"
#include "epg.h"
#include "dispatch.h"
-#include "htsclient.h"
#include "htsp.h"
#include "autorec.h"
now = dispatch_clock;
- RB_FOREACH(ch, &channel_tree, ch_global_link) {
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
/* Age out any old events */
regcomp(&preg, title, REG_ICASE | REG_EXTENDED | REG_NOSUB))
return -1;
- RB_FOREACH(ch, &channel_tree, ch_global_link) {
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
if(LIST_FIRST(&ch->ch_transports) == NULL)
continue;
if(ex == NULL)
break;
- RB_FOREACH(ch, &channel_tree, ch_global_link) {
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
ec = epg_event_find_by_time0(&ch->ch_epg_events, now);
cnt = 0;
continue;
if(xc->xc_channel != NULL) {
- ch = channel_find(xc->xc_channel, 0);
+ ch = channel_find_by_name(xc->xc_channel, 0);
if(ch == NULL)
continue;
} else {
how = 0;
- ch = channel_find(xc->xc_displayname, 0);
+ ch = channel_find_by_name(xc->xc_displayname, 0);
if(ch == NULL) {
ch = xmltv_resolve_by_events(xc);
#include "tvhead.h"
#include "channels.h"
#include "subscriptions.h"
-#include "htsclient.h"
#include "ffmuxer.h"
#include "buffer.h"
if((s = config_get_str_sub(&ce->ce_sub, "channel", NULL)) == NULL)
continue;
- ch = channel_find(s, 1);
+ ch = channel_find_by_name(s, 1);
t->tht_name = strdup(ch->ch_name);
+++ /dev/null
-/*
- * tvheadend, (simple) client interface
- * Copyright (C) 2007 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 <assert.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-
-#include "tvhead.h"
-#include "channels.h"
-#include "subscriptions.h"
-#include "pvr.h"
-#include "epg.h"
-#include "teletext.h"
-#include "dispatch.h"
-#include "buffer.h"
-#include "tsmux.h"
-#include "tcp.h"
-#include "htsclient.h"
-
-LIST_HEAD(client_list, client);
-
-struct client_list all_clients;
-
-
-/*
- * Client
- */
-
-typedef struct client {
- tcp_session_t c_tcp_session;
-
- LIST_ENTRY(client) c_global_link;
- int c_streamfd;
- pthread_t c_ptid;
-
- LIST_HEAD(, th_subscription) c_subscriptions;
-
- struct in_addr c_ipaddr;
- int c_port;
-
- struct ref_update_queue c_refq;
-
- dtimer_t c_status_timer;
-
- void *c_muxer;
-
-} client_t;
-
-
-
-void client_status_update(void *aux, int64_t now);
-
-#define cprintf(c, fmt...) tcp_printf(&(c)->c_tcp_session, fmt)
-
-
-static void
-client_output_ts(void *opaque, th_subscription_t *s,
- uint8_t *pkt, int blocks, int64_t pcr)
-{
- struct msghdr msg;
- struct iovec vec[2];
- int r;
- client_t *c = opaque;
- struct sockaddr_in sin;
- char hdr[2];
-
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(c->c_port);
- sin.sin_addr = c->c_ipaddr;
-
- hdr[0] = HTSTV_TRANSPORT_STREAM;
- hdr[1] = s->ths_u32;
-
- vec[0].iov_base = hdr;
- vec[0].iov_len = 2;
- vec[1].iov_base = pkt;
- vec[1].iov_len = blocks * 188;
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_name = &sin;
- msg.msg_namelen = sizeof(struct sockaddr_in);
- msg.msg_iov = vec;
- msg.msg_iovlen = 2;
-
- r = sendmsg(c->c_streamfd, &msg, 0);
- if(r < 0)
- perror("sendmsg");
-}
-
-
-/*
- *
- */
-
-static int
-cr_channel_info(client_t *c, char **argv, int argc)
-{
- channel_t *ch;
-
- if(argc < 1)
- return 1;
-
- if((ch = channel_by_index(atoi(argv[0]))) == NULL)
- return 1;
-
- cprintf(c,
- "displayname = %s\n"
- "icon = %s\n"
- "tag = %d\n",
- ch->ch_name,
- ch->ch_icon ? ch->ch_icon : "",
- ch->ch_tag);
-
- return 0;
-}
-
-
-/*
- *
- */
-
-static int
-cr_channel_unsubscribe(client_t *c, char **argv, int argc)
-{
- th_subscription_t *s;
- int chindex;
-
- if(argc < 1)
- return 1;
-
- chindex = atoi(argv[0]);
-
- LIST_FOREACH(s, &c->c_subscriptions, ths_subscriber_link) {
- if(s->ths_u32 == chindex)
- break;
- }
-
- if(s == NULL)
- return 1;
-
- LIST_REMOVE(s, ths_subscriber_link);
- subscription_unsubscribe(s);
- return 0;
-}
-
-
-/*
- * Called when a subscription gets/loses access to a transport
- */
-static void
-client_subscription_callback(struct th_subscription *s,
- subscription_event_t event, void *opaque)
-{
- client_t *c = opaque;
-
- switch(event) {
- case TRANSPORT_AVAILABLE:
- assert(c->c_muxer == NULL);
- c->c_muxer = ts_muxer_init(s, client_output_ts, c,
- TS_HTSCLIENT | TS_SEEK, 0);
- ts_muxer_play(c->c_muxer, 0);
- break;
-
- case TRANSPORT_UNAVAILABLE:
- assert(c->c_muxer != NULL);
- ts_muxer_deinit(c->c_muxer, s);
- c->c_muxer = NULL;
- break;
- }
-}
-
-/*
- *
- */
-static int
-cr_channel_subscribe(client_t *c, char **argv, int argc)
-{
- channel_t *ch;
- th_subscription_t *s;
- unsigned int chindex, weight;
- char tmp[100];
- struct sockaddr_in *si;
-
- if(argc < 1)
- return 1;
-
- chindex = atoi(argv[0]);
-
- weight = argc > 1 ? atoi(argv[1]) : 100;
-
- LIST_FOREACH(s, &c->c_subscriptions, ths_subscriber_link) {
- if(s->ths_u32 == chindex) {
- subscription_set_weight(s, weight);
- return 0;
- }
- }
-
- if((ch = channel_by_index(chindex)) == NULL)
- return 1;
-
- si = (struct sockaddr_in *)&c->c_tcp_session.tcp_peer_addr;
-
- snprintf(tmp, sizeof(tmp), "HTS Client @ %s",
- inet_ntoa(si->sin_addr));
-
- s = subscription_create(ch, weight, tmp, client_subscription_callback, c, 0);
- if(s == NULL)
- return 1;
-
- s->ths_u32 = ch->ch_index;
- LIST_INSERT_HEAD(&c->c_subscriptions, s, ths_subscriber_link);
- return 0;
-}
-
-
-/*
- *
- */
-
-static int
-cr_channels_list(client_t *c, char **argv, int argc)
-{
- channel_t *ch;
-
- RB_FOREACH(ch, &channel_tree, ch_global_link) {
- cprintf(c, "channel = %d\n", ch->ch_index);
- }
- return 0;
-}
-
-
-/*
- *
- */
-
-static int
-cr_streamport(client_t *c, char **argv, int argc)
-{
- if(argc < 2)
- return 1;
-
- if(c->c_streamfd == -1)
- c->c_streamfd = socket(AF_INET, SOCK_DGRAM, 0);
-
- c->c_ipaddr.s_addr = inet_addr(argv[0]);
- c->c_port = atoi(argv[1]);
-
- tvhlog(LOG_INFO, "htsclient",
- "%s registers UDP stream target %s:%d",
- tcp_logname(&c->c_tcp_session), inet_ntoa(c->c_ipaddr), c->c_port);
-
- return 0;
-}
-
-/*
- *
- */
-
-static int
-cr_event_info(client_t *c, char **argv, int argc)
-{
- event_t *e = NULL, *x;
- uint32_t tag, prev, next;
- channel_t *ch;
- pvr_rec_t *pvrr;
-
- if(argc < 2)
- return 1;
-
- if(!strcasecmp(argv[0], "tag"))
- e = epg_event_find_by_tag(atoi(argv[1]));
- if(!strcasecmp(argv[0], "now"))
- if((ch = channel_by_index(atoi(argv[1]))) != NULL)
- e = epg_event_get_current(ch);
- if(!strcasecmp(argv[0], "at") && argc == 3)
- if((ch = channel_by_index(atoi(argv[1]))) != NULL)
- e = epg_event_find_by_time(ch, atoi(argv[2]));
-
- if(e == NULL) {
- return 1;
- }
-
- tag = e->e_tag;
- x = TAILQ_PREV(e, event_queue, e_channel_link);
- prev = x != NULL ? x->e_tag : 0;
-
- x = TAILQ_NEXT(e, e_channel_link);
- next = x != NULL ? x->e_tag : 0;
-
- pvrr = pvr_get_by_entry(e);
-
- cprintf(c,
- "start = %ld\n"
- "stop = %ld\n"
- "title = %s\n"
- "desc = %s\n"
- "tag = %u\n"
- "prev = %u\n"
- "next = %u\n"
- "pvrstatus = %d\n",
-
- e->e_start,
- e->e_start + e->e_duration,
- e->e_title ?: "",
- e->e_desc ?: "",
- tag,
- prev,
- next,
- pvrr != NULL ? pvrr->pvrr_status : HTSTV_PVR_STATUS_NONE);
-
- return 0;
-}
-
-/*
- *
- */
-
-static int
-cr_event_record(client_t *c, char **argv, int argc)
-{
- event_t *e;
-
- if(argc < 1)
- return 1;
-
- e = epg_event_find_by_tag(atoi(argv[0]));
- if(e == NULL) {
- return 1;
- }
-
- pvr_schedule_by_event(e, "htsclient");
-
- return 0;
-}
-
-
-
-
-/*
- *
- */
-static int
-cr_channel_record(client_t *c, char **argv, int argc)
-{
- channel_t *ch;
- int duration;
-
- if(argc < 2)
- return 1;
-
- if((ch = channel_by_index(atoi(argv[0]))) == NULL)
- return 1;
-
- duration = atoi(argv[1]);
-
- pvr_schedule_by_channel_and_time(ch, duration, "htsclient");
- return 0;
-}
-
-/*
- *
- */
-static int
-cr_pvr_entry(client_t *c, pvr_rec_t *pvrr)
-{
- event_t *e;
-
- if(pvrr == NULL)
- return 1;
-
- cprintf(c,
- "title = %s\n"
- "start = %ld\n"
- "stop = %ld\n"
- "desc = %s\n"
- "pvr_tag = %d\n"
- "pvrstatus = %d\n"
- "filename = %s\n"
- "channel = %d\n",
- pvrr->pvrr_title ?: "",
- pvrr->pvrr_start,
- pvrr->pvrr_stop,
- pvrr->pvrr_desc ?: "",
- pvrr->pvrr_ref,
- pvrr->pvrr_status,
- pvrr->pvrr_filename,
- pvrr->pvrr_channel->ch_index);
-
-
- e = epg_event_find_by_time(pvrr->pvrr_channel, pvrr->pvrr_start);
- if(e != NULL)
- cprintf(c, "event_tag = %d\n", e->e_tag);
-
- return 0;
-}
-
-/*
- *
- */
-
-static int
-cr_pvr_getlog(client_t *c, char **argv, int argc)
-{
- pvr_rec_t *pvrr;
-
- if(argc < 1)
- return 1;
-
- pvrr = pvr_get_log_entry(atoi(argv[0]));
- return cr_pvr_entry(c, pvrr);
-}
-
-
-/*
- *
- */
-
-static int
-cr_pvr_gettag(client_t *c, char **argv, int argc)
-{
- pvr_rec_t *pvrr;
-
- if(argc < 1)
- return 1;
-
- pvrr = pvr_get_tag_entry(atoi(argv[0]));
- return cr_pvr_entry(c, pvrr);
-}
-
-
-/*
- *
- */
-
-const struct {
- const char *name;
- int (*func)(client_t *c, char *argv[], int argc);
-} cr_cmds[] = {
- { "streamport", cr_streamport },
- { "channels.list", cr_channels_list },
- { "channel.info", cr_channel_info },
- { "channel.subscribe", cr_channel_subscribe },
- { "channel.unsubscribe", cr_channel_unsubscribe },
- { "channel.record", cr_channel_record },
- { "event.info", cr_event_info },
- { "event.record", cr_event_record },
- { "pvr.getlog", cr_pvr_getlog },
- { "pvr.gettag", cr_pvr_gettag },
-};
-
-
-static int
-client_req(void *aux, char *buf)
-{
- client_t *c = aux;
- int i, l, x;
- const char *n;
- char *argv[40];
- int argc = 0;
-
- for(i = 0; i < sizeof(cr_cmds) / sizeof(cr_cmds[0]); i++) {
- n = cr_cmds[i].name;
- l = strlen(n);
- if(!strncasecmp(buf, n, l) && (buf[l] == ' ' || buf[l] == 0)) {
- buf += l;
-
- while(*buf) {
- if(*buf < 33) {
- buf++;
- continue;
- }
- argv[argc++] = buf;
- while(*buf > 32)
- buf++;
- if(*buf == 0)
- break;
- *buf++ = 0;
- }
- x = cr_cmds[i].func(c, argv, argc);
-
- if(x >= 0)
- cprintf(c, "eom %s\n", x ? "error" : "ok");
-
- return 0;
- }
- }
- cprintf(c, "eom nocommand\n");
- return 0;
-}
-
-/*
- * client disconnect
- */
-static void
-client_disconnect(client_t *c)
-{
- th_subscription_t *s;
-
- dtimer_disarm(&c->c_status_timer);
-
- if(c->c_streamfd != -1)
- close(c->c_streamfd);
-
- LIST_REMOVE(c, c_global_link);
-
- while((s = LIST_FIRST(&c->c_subscriptions)) != NULL) {
- LIST_REMOVE(s, ths_subscriber_link);
- subscription_unsubscribe(s);
- }
-}
-
-
-/*
- *
- */
-static void
-htsclient_tcp_callback(tcpevent_t event, void *tcpsession)
-{
- client_t *c = tcpsession;
-
- switch(event) {
- case TCP_CONNECT:
- TAILQ_INIT(&c->c_refq);
- LIST_INSERT_HEAD(&all_clients, c, c_global_link);
- c->c_streamfd = -1;
- break;
-
- case TCP_DISCONNECT:
- client_disconnect(c);
- break;
-
- case TCP_INPUT:
- tcp_line_read(&c->c_tcp_session, client_req);
- break;
- }
-}
-
-
-/*
- * Fire up client handling
- */
-
-void
-client_start(void)
-{
- tcp_create_server(9909, sizeof(client_t), "htsclient",
- htsclient_tcp_callback);
-}
-
+++ /dev/null
-/*
- * tvheadend, (simple) client interface
- * Copyright (C) 2007 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/>.
- */
-
-#ifndef HTSCLIENT_H_
-#define HTSCLIENT_H_
-
-void client_start(void);
-
-void clients_send_ref(int ref);
-
-#endif /* HTSCLIENT_H_ */
htsmsg_add_str(msg, "method", method);
htsmsg_add_str(msg, "channelName", ch->ch_name);
- htsmsg_add_u32(msg, "channelTag", ch->ch_tag);
+ htsmsg_add_u32(msg, "channelId", ch->ch_id);
if(ch->ch_icon != NULL)
htsmsg_add_str(msg, "channelIcon", ch->ch_icon);
htsmsg_t *msg;
channel_t *ch;
- RB_FOREACH(ch, &channel_tree, ch_global_link) {
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
if(LIST_FIRST(&ch->ch_transports) == NULL)
continue;
channel_t *ch;
th_subscription_t *s;
htsmsg_t *r;
+ uint32_t u32;
- uint32_t tag;
+ if(htsmsg_get_u32(in, "channelId", &u32))
+ return rpc_error(ses, "missing argument: channelId");
- if(htsmsg_get_u32(in, "channelTag", &tag))
- return rpc_error(ses, "missing argument: channelTag");
-
- if((ch = channel_by_tag(tag)) == NULL)
+ if((ch = channel_find_by_identifier(u32)) == NULL)
return rpc_error(ses, "Channel not found");
LIST_FOREACH(s, &htsp->htsp_subscriptions, ths_subscriber_link) {
*/
htsmsg_add_str(m, "method", "muxpkt");
- htsmsg_add_u32(m, "channelTag", s->ths_channel->ch_tag);
+ htsmsg_add_u32(m, "channelId", s->ths_channel->ch_id);
htsmsg_add_u64(m, "stream", tms->tms_index);
htsmsg_add_u64(m, "dts", pkt->pkt_dts);
m = htsmsg_create();
htsmsg_add_str(m, "method", "subscription_start");
- htsmsg_add_u32(m, "channelTag", s->ths_channel->ch_tag);
+ htsmsg_add_u32(m, "channelId", s->ths_channel->ch_id);
LIST_FOREACH(tms, &tm->tm_streams, tms_muxer_link0) {
tms->tms_index = index++;
if(htsp->htsp_zombie == 0) {
m = htsmsg_create();
htsmsg_add_str(m, "method", "subscription_stop");
- htsmsg_add_u32(m, "channelTag", s->ths_channel->ch_tag);
+ htsmsg_add_u32(m, "channelId", s->ths_channel->ch_id);
htsmsg_add_str(m, "reason", "unknown");
htsp_send_msg(htsp, m, 0);
th_subscription_t *s;
LIST_FOREACH(s, &htsp->htsp_subscriptions, ths_subscriber_link) {
- if(s->ths_channel->ch_tag == id)
+ if(s->ths_channel->ch_id == id)
break;
}
if((name = config_get_str_sub(head, "channel", NULL)) == NULL)
return;
- ch = channel_find(name, 1);
+ ch = channel_find_by_name(name, 1);
om = calloc(1, sizeof(output_multicast_t));
#include "dvb/dvb.h"
#include "v4l.h"
#include "channels.h"
-#include "htsclient.h"
#include "epg.h"
#include "epg_xmltv.h"
#include "pvr.h"
pvr_init();
output_multicast_setup();
- client_start();
p = atoi(config_get_str("http-server-port", "9981"));
if(p)
st = transport_add_stream(t, pid, type);
st->st_tb = (AVRational){1, 90000};
- if((v = htsmsg_get_str(c, "lang")) != NULL)
+ if((v = htsmsg_get_str(c, "language")) != NULL)
av_strlcpy(st->st_lang, v, 4);
if(!htsmsg_get_u32(c, "frameduration", &u32))
#include "tvhead.h"
#include "channels.h"
#include "subscriptions.h"
-#include "htsclient.h"
#include "pvr.h"
#include "epg.h"
#include "dispatch.h"
if(channel != NULL && start && stop && title && status) {
pvrr = calloc(1, sizeof(pvr_rec_t));
- pvrr->pvrr_channel = channel_find(channel, 1);
+ pvrr->pvrr_channel = channel_find_by_name(channel, 1);
pvrr->pvrr_start = start;
pvrr->pvrr_stop = stop;
pvrr->pvrr_status = *status;
m = htsmsg_create();
htsmsg_add_str(m, "name", ch->ch_name);
- htsmsg_add_u32(m, "tag", ch->ch_tag);
+ htsmsg_add_u32(m, "id", ch->ch_id);
if(ch->ch_icon)
htsmsg_add_str(m, "icon", ch->ch_icon);
return m;
out = htsmsg_create();
htsmsg_add_u32(out, "seq", ses->rs_seq);
- RB_FOREACH(ch, &channel_tree, ch_global_link)
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link)
htsmsg_add_msg(out, "channel", rpc_build_channel_msg(ch));
return out;
if(htsmsg_get_u32(in, "tag", &u32) >= 0) {
e = epg_event_find_by_tag(u32);
} else if((s = htsmsg_get_str(in, "channel")) != NULL) {
- if((ch = channel_find(s, 0)) == NULL) {
+ if((ch = channel_find_by_name(s, 0)) == NULL) {
errtxt = "Channel not found";
} else {
if(htsmsg_get_u32(in, "time", &u32) < 0) {
return NULL;
c++;
- RB_FOREACH(ch, &channel_tree, ch_global_link)
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link)
if(!strcasecmp(ch->ch_sname, c))
return ch;
tvhlog(LOG_INFO, "serviceprobe", "Probed \"%s\" -- Ok", t->tht_svcname);
if(t->tht_ch == NULL && t->tht_svcname != NULL) {
- ch = channel_find(t->tht_svcname, 1);
+ ch = channel_find_by_name(t->tht_svcname, 1);
transport_map_channel(t, ch);
t->tht_config_change(t);
if(ch == NULL) {
if(t->tht_chname == NULL)
return;
- ch = channel_find(t->tht_chname, 1);
+ ch = channel_find_by_name(t->tht_chname, 1);
} else {
free(t->tht_chname);
t->tht_chname = strdup(ch->ch_name);
extern time_t dispatch_clock;
extern int startupcounter;
extern struct th_transport_list all_transports;
-extern struct channel_tree channel_tree;
+extern struct channel_tree channel_name_tree;
extern struct pvr_rec_list pvrr_global_list;
extern struct th_subscription_list subscriptions;
void (*tht_config_change)(struct th_transport *t);
+ const char *(*tht_networkname)(struct th_transport *t);
+
const char *(*tht_sourcename)(struct th_transport *t);
int (*tht_quality_index)(struct th_transport *t);
*/
typedef struct channel {
- RB_ENTRY(channel) ch_global_link;
+ RB_ENTRY(channel) ch_name_link;
+ char *ch_name;
+ char *ch_sname;
+ RB_ENTRY(channel) ch_identifier_link;
+ int ch_id;
LIST_HEAD(, th_transport) ch_transports;
LIST_HEAD(, th_subscription) ch_subscriptions;
- int ch_index;
-
- const char *ch_name;
- const char *ch_sname;
-
struct tt_decoder ch_tt;
- int ch_tag;
-
enum {
COMMERCIAL_DETECT_NONE,
COMMERCIAL_DETECT_TTP192,
#include "webui.h"
#include "access.h"
#include "dtable.h"
+#include "channels.h"
+#include "psi.h"
#include "dvb/dvb.h"
#include "dvb/dvb_support.h"
extjs_load(hq, "static/app/acleditor.js");
extjs_load(hq, "static/app/cwceditor.js");
extjs_load(hq, "static/app/dvb.js");
+ extjs_load(hq, "static/app/chconf.js");
/**
* Finally, the app itself
return 0;
}
+
+/**
+ *
+ */
+static int
+extjs_chlist(http_connection_t *hc, http_reply_t *hr,
+ const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hr->hr_q;
+ htsmsg_t *out, *array, *c;
+ channel_t *ch;
+
+ out = htsmsg_create();
+
+ array = htsmsg_create_array();
+
+ RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
+ c = htsmsg_create();
+ htsmsg_add_str(c, "name", ch->ch_name);
+ htsmsg_add_u32(c, "chid", ch->ch_id);
+ htsmsg_add_msg(array, NULL, c);
+ }
+
+ htsmsg_add_msg(out, "entries", array);
+
+ htsmsg_json_serialize(out, hq, 0);
+ htsmsg_destroy(out);
+ http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
+ return 0;
+}
+
+
/**
*
*/
array = htsmsg_create_array();
htsmsg_add_msg(array, NULL, rec);
- htsmsg_add_msg(out, "dvbadapters", array);
+ htsmsg_add_msg(out, root, array);
return out;
}
return 0;
}
+/**
+ *
+ */
+static htsmsg_t *
+build_transport_msg(th_transport_t *t)
+{
+ htsmsg_t *r = htsmsg_create();
+ th_stream_t *st;
+
+ char video[200];
+ char audio[200];
+ char subtitles[200];
+ char scrambling[200];
+
+ htsmsg_add_u32(r, "enabled", !t->tht_disabled);
+ htsmsg_add_str(r, "name", t->tht_svcname);
+
+ htsmsg_add_str(r, "provider", t->tht_provider ?: "");
+ htsmsg_add_str(r, "network", t->tht_networkname(t));
+ htsmsg_add_str(r, "source", t->tht_sourcename(t));
+
+ htsmsg_add_str(r, "status", transport_status_to_text(t->tht_last_status));
+
+ video[0] = 0;
+ audio[0] = 0;
+ subtitles[0] = 0;
+ scrambling[0] = 0;
+
+ LIST_FOREACH(st, &t->tht_streams, st_link) {
+ switch(st->st_type) {
+ case HTSTV_TELETEXT:
+ case HTSTV_SUBTITLES:
+ case HTSTV_PAT:
+ case HTSTV_PMT:
+ break;
+
+ case HTSTV_MPEG2VIDEO:
+ snprintf(video + strlen(video), sizeof(video) - strlen(video),
+ "%sMPEG-2 (PID:%d", strlen(video) > 0 ? ", " : "",
+ st->st_pid);
+
+ video:
+ if(st->st_frame_duration) {
+ snprintf(video + strlen(video), sizeof(video) - strlen(video),
+ ", %d Hz)", 90000 / st->st_frame_duration);
+ } else {
+ snprintf(video + strlen(video), sizeof(video) - strlen(video),
+ ")");
+ }
+
+ break;
+
+ case HTSTV_H264:
+ snprintf(video + strlen(video), sizeof(video) - strlen(video),
+ "%sH.264 (PID:%d", strlen(video) > 0 ? ", " : "",
+ st->st_pid);
+ goto video;
+
+ case HTSTV_MPEG2AUDIO:
+ snprintf(audio + strlen(audio), sizeof(audio) - strlen(audio),
+ "%sMPEG-2 (PID:%d", strlen(audio) > 0 ? ", " : "",
+ st->st_pid);
+ audio:
+ if(st->st_lang[0]) {
+ snprintf(audio + strlen(audio), sizeof(audio) - strlen(audio),
+ ", languange: \"%s\")", st->st_lang);
+ } else {
+ snprintf(audio + strlen(audio), sizeof(audio) - strlen(audio),
+ ")");
+ }
+ break;
+
+ case HTSTV_AC3:
+ snprintf(audio + strlen(audio), sizeof(audio) - strlen(audio),
+ "%sAC3 (PID:%d", strlen(audio) > 0 ? ", " : "",
+ st->st_pid);
+ goto audio;
+
+ case HTSTV_CA:
+ snprintf(scrambling + strlen(scrambling),
+ sizeof(scrambling) - strlen(scrambling),
+ "%s%s", strlen(scrambling) > 0 ? ", " : "",
+ psi_caid2name(st->st_caid));
+ break;
+ }
+ }
+
+ htsmsg_add_str(r, "video", video);
+ htsmsg_add_str(r, "audio", audio);
+ htsmsg_add_str(r, "scrambling", scrambling[0] ? scrambling : "none");
+ return r;
+}
+
+
+/**
+ *
+ */
+static int
+extjs_channel(http_connection_t *hc, http_reply_t *hr,
+ const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hr->hr_q;
+ const char *s = http_arg_get(&hc->hc_req_args, "chid");
+ const char *op = http_arg_get(&hc->hc_req_args, "op");
+ channel_t *ch = s ? channel_find_by_identifier(atoi(s)) : NULL;
+ channel_t *ch2;
+ th_transport_t *t;
+ int reloadchlist = 0;
+ htsmsg_t *out, *array, *r;
+
+ if(ch == NULL)
+ return HTTP_STATUS_BAD_REQUEST;
+
+ if(!strcmp(op, "load")) {
+ r = htsmsg_create();
+ htsmsg_add_u32(r, "id", ch->ch_id);
+ htsmsg_add_str(r, "name", ch->ch_name);
+ htsmsg_add_str(r, "comdetect", "tt192");
+ out = json_single_record(r, "channels");
+
+ } else if(!strcmp(op, "gettransports")) {
+ out = htsmsg_create();
+ array = htsmsg_create_array();
+ LIST_FOREACH(t, &ch->ch_transports, tht_ch_link)
+ htsmsg_add_msg(array, NULL, build_transport_msg(t));
+
+ htsmsg_add_msg(out, "entries", array);
+
+ } else if(!strcmp(op, "delete")) {
+
+ channel_delete(ch);
+
+ out = htsmsg_create();
+ htsmsg_add_u32(out, "reloadchlist", 1);
+ htsmsg_add_u32(out, "success", 1);
+
+ } else if(!strcmp(op, "mergefrom")) {
+
+ if((s = http_arg_get(&hc->hc_req_args, "srcch")) == NULL)
+ return HTTP_STATUS_BAD_REQUEST;
+
+ ch2 = channel_find_by_identifier(atoi(s));
+ if(ch2 == NULL || ch2 == ch)
+ return HTTP_STATUS_BAD_REQUEST;
+
+ channel_merge(ch, ch2); /* ch2 goes away here */
+
+ out = htsmsg_create();
+ htsmsg_add_u32(out, "reloadchlist", 1);
+ htsmsg_add_u32(out, "success", 1);
+
+
+ } else if(!strcmp(op, "save")) {
+
+ if((s = http_arg_get(&hc->hc_req_args, "name")) != NULL &&
+ strcmp(s, ch->ch_name)) {
+
+ if(channel_rename(ch, s)) {
+ out = htsmsg_create();
+ htsmsg_add_u32(out, "success", 0);
+ htsmsg_add_str(out, "errormsg", "Channel name already exist");
+ goto response;
+ } else {
+ reloadchlist = 1;
+ }
+ }
+
+ out = htsmsg_create();
+ htsmsg_add_u32(out, "reloadchlist", 1);
+ htsmsg_add_u32(out, "success", 1);
+
+ } else {
+ return HTTP_STATUS_BAD_REQUEST;
+ }
+ response:
+ htsmsg_json_serialize(out, hq, 0);
+ htsmsg_destroy(out);
+
+ http_output(hc, hr, "text/x-json; charset=UTF-8", NULL, 0);
+ return 0;
+}
+
/**
* WEB user interface
*/
http_path_add("/dvbtree", NULL, extjs_dvbtree, ACCESS_WEB_INTERFACE);
http_path_add("/dvbadapter", NULL, extjs_dvbadapter, ACCESS_WEB_INTERFACE);
http_path_add("/dvbnetworks", NULL, extjs_dvbnetworks, ACCESS_WEB_INTERFACE);
+ http_path_add("/chlist", NULL, extjs_chlist, ACCESS_WEB_INTERFACE);
+ http_path_add("/channel", NULL, extjs_channel, ACCESS_WEB_INTERFACE);
}
--- /dev/null
+/**
+ * Channel details
+ */
+tvheadend.channeldetails = function(chid, chname) {
+ var fm = Ext.form;
+ var xg = Ext.grid;
+
+ var expander = new xg.RowExpander({
+ tpl : new Ext.Template(
+ '<div><b width=100px>Video:</b>{video}</div>',
+ '<div><b>Audio:</b>{audio}</div>',
+ '<div><b>Subtitling:</b>{subtitles}</div>',
+ '<div><b>Scrambling:</b>{scrambling}</div>'
+ )
+ });
+
+ var enabledColumn = new Ext.grid.CheckColumn({
+ header: "Enabled",
+ dataIndex: 'enabled',
+ width: 60
+ });
+
+ var cm = new Ext.grid.ColumnModel([expander,
+ enabledColumn,
+ {
+ width: 125,
+ id:'name',
+ header: "Original name",
+ dataIndex: 'name',
+ },{
+ width: 125,
+ id:'status',
+ header: "Last status",
+ dataIndex: 'status',
+ },{
+ width: 125,
+ id:'provider',
+ header: "Provider",
+ dataIndex: 'provider',
+ },{
+ width: 125,
+ id:'network',
+ header: "Network",
+ dataIndex: 'network',
+ },{
+ width: 250,
+ id:'source',
+ header: "Source",
+ dataIndex: 'source',
+ }
+ ]);
+
+
+ var transportRecord = Ext.data.Record.create([
+ {name: 'enabled'},
+ {name: 'status'},
+ {name: 'name'},
+ {name: 'provider'},
+ {name: 'network'},
+ {name: 'source'},
+ {name: 'video'},
+ {name: 'audio'},
+ {name: 'scrambling'},
+ {name: 'subtitles'}
+ ]);
+
+ var transportsstore =
+ new Ext.data.JsonStore({root: 'entries',
+ fields: transportRecord,
+ url: "channel",
+ autoLoad: true,
+ id: 'id',
+ storeid: 'id',
+ baseParams: {chid: chid, op: "gettransports"}
+ });
+
+
+ var transportsgrid = new Ext.grid.EditorGridPanel({
+ title:'Transports',
+ anchor: '100% 50%',
+ plugins:[enabledColumn, expander],
+ store: transportsstore,
+ clicksToEdit: 2,
+ cm: cm,
+ selModel: new Ext.grid.RowSelectionModel({singleSelect:false})
+ });
+
+
+ var confreader = new Ext.data.JsonReader({
+ root: 'channels',
+ }, ['name', 'comdetect']);
+
+ var confpanel = new Ext.FormPanel({
+ frame:true,
+ disabled:true,
+ anchor: '100% 50%',
+ labelAlign: 'right',
+ labelWidth: 150,
+ waitMsgTarget: true,
+ reader: confreader,
+ // defaultType: 'textfield',
+
+ items: [{
+ layout:'column',
+ items:[{
+ columnWidth:.5,
+ layout: 'form',
+ defaultType: 'textfield',
+ items: [{
+
+ fieldLabel: 'Channel name',
+ name: 'name',
+ }
+ /*
+ ,
+ new Ext.form.ComboBox({
+ allowBlank: false,
+ fieldLabel: 'Commercial detection',
+ name: 'comdetect',
+ displayField:'mode',
+ valueField:'imode',
+ mode: 'local',
+ triggerAction: 'all',
+ selectOnFocus:true,
+ editable:false,
+ store: new Ext.data.SimpleStore({
+ fields: ['imode', 'mode'],
+ data: [
+ ['none', 'None'],
+ ['tt192', 'Teletext page 192']]
+ })
+ })
+ */
+ ]
+ }
+ /*
+ ,{
+ columnWidth:.5,
+ layout: 'form',
+ items: [{
+ xtype: 'checkboxgroup',
+ fieldLabel: 'Tags',
+ itemCls: 'x-check-group-alt',
+ columns: 1,
+ vertical: true,
+ items: [{
+ boxLabel: 'Favourites', name: 'favourite'},{
+ boxLabel: 'Sports', name: 'sports'},{
+ boxLabel: 'News', name: 'news'},{
+ boxLabel: 'Movies', name: 'movies'},{
+ boxLabel: 'Children', name: 'children'}
+ ]
+ }
+ ]
+ } */
+ ]
+ }]
+ });
+
+ confpanel.getForm().load({url:'/channel',
+ params:{'chid': chid, 'op':'load'},
+ success:function(form, action) {
+ confpanel.enable();
+ }});
+
+
+ function saveChanges() {
+ confpanel.getForm().submit({url:'/channel',
+ params:{'chid': chid, 'op':'save'},
+ waitMsg:'Saving Data...',
+
+ success: function(form, action) {
+
+ if(action.result.reloadchlist) {
+ tvheadend.chconfliststore.reload();
+ }
+ },
+
+ failure: function(form, action) {
+ Ext.Msg.alert('Save failed', action.result.errormsg);
+ }
+ });
+ }
+
+ function deleteChannel() {
+ Ext.MessageBox.confirm('Message',
+ 'Do you really want to delete "' + chname + '"',
+ function(button) {
+ if(button == 'no')
+ return;
+ Ext.Ajax.request({url: '/channel',
+ params:{'chid': chid, 'op':'delete'},
+ success: function() {
+ tvheadend.chconfliststore.reload();
+ panel.destroy();
+ }
+ });
+ }
+ );
+ }
+
+ var panel = new Ext.Panel({
+ title: chname,
+ border:false,
+ tbar: [{
+ text: "Save changes",
+ handler: saveChanges
+ },{
+ text: "Delete channel",
+ handler: deleteChannel
+ }],
+ defaults: {
+ border:false,
+ },
+ layout:'anchor',
+ items: [confpanel,transportsgrid]
+ });
+
+
+ panel.on('afterlayout', function(parent, n) {
+ var DropTargetEl = parent.body.dom;
+
+ var DropTarget = new Ext.dd.DropTarget(DropTargetEl, {
+ ddGroup : 'chconfddgroup',
+ notifyEnter : function(ddSource, e, data) {
+
+ //Add some flare to invite drop.
+ parent.body.stopFx();
+ parent.body.highlight();
+ },
+ notifyDrop : function(ddSource, e, data){
+
+ // Reference the record (single selection) for readability
+ var selectedRecord = ddSource.dragData.selections[0];
+
+ Ext.MessageBox.confirm('Merge channels',
+ 'Copy transport configuration from "' + selectedRecord.data.name +
+ '" to "' + chname + '". This will also remove the channel "' +
+ selectedRecord.data.name + '"',
+ function(button) {
+ if(button == 'no')
+ return;
+ Ext.Ajax.request({url: '/channel',
+ params:{chid: chid,
+ op:'mergefrom',
+ srcch: selectedRecord.data.chid},
+ success: function() {
+ transportsstore.reload();
+ tvheadend.chconfliststore.reload();
+ }});
+ }
+ );
+ }
+ });
+ });
+ return panel;
+ }
+
+
+/**
+ *
+ */
+tvheadend.chconf = function() {
+
+ var ChannelRecord = Ext.data.Record.create([
+ {name: 'name'},
+ {name: 'chid'}]);
+
+ var store = new Ext.data.JsonStore({root: 'entries',
+ fields: ChannelRecord,
+ url: "chlist",
+ autoLoad: true,
+ id: 'id',
+ storeid: 'id'
+ });
+
+
+ var chlist = new Ext.grid.GridPanel({
+ ddGroup: 'chconfddgroup',
+ enableDragDrop: true,
+ stripeRows:true,
+ region:'west',
+ width: 300,
+ columns: [{id:'name',
+ header: "Channel name",
+ width: 260,
+ dataIndex: 'name'}
+ ],
+ selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
+ store: store,
+ });
+
+ var details = new Ext.Panel({
+ region:'center', layout:'fit',
+ items:[{border: false}]
+ });
+
+
+ var panel = new Ext.Panel({
+ listeners: {activate: handleActivate},
+ border: false,
+ title:'Channels',
+ layout:'border',
+ items: [chlist, details]
+ });
+
+ function handleActivate(tab){
+ store.reload();
+ }
+
+ chlist.on('rowclick', function(grid, n) {
+ var rec = store.getAt(n);
+
+ details.remove(details.getComponent(0));
+ details.doLayout();
+
+ var newpanel = new tvheadend.channeldetails(rec.data.chid,
+ rec.data.name);
+
+ details.add(newpanel);
+ details.doLayout();
+ });
+
+
+ /**
+ * Setup Drop Targets
+ */
+
+ // This will make sure we only drop to the view container
+
+ /*
+ var DropTargetEl = details.getView();
+
+ var DropTarget = new Ext.dd.DropTarget(DropTargetEl, {
+ ddGroup : 'chconfddgroup',
+ notifyEnter : function(ddSource, e, data) {
+
+ //Add some flare to invite drop.
+ panel.body.stopFx();
+ panel.body.highlight();
+ },
+ notifyDrop : function(ddSource, e, data){
+
+ // Reference the record (single selection) for readability
+ var selectedRecord = ddSource.dragData.selections[0];
+
+ console.log(selectedRecord);
+ }
+ });
+
+ */
+ /*
+ details.on('afterlayout', function(parent, n) {
+ console.log(parent);
+ });
+ */
+
+ tvheadend.chconfliststore = store;
+
+ return panel;
+}
\ No newline at end of file
border: false,
items:[status, confpanel],
tbar:[{
+ iconCls:'add',
text: 'Add mux(es)',
handler: addmux
},{
}
}
});
+
+
+
+
+Ext.grid.RowExpander = function(config){
+ Ext.apply(this, config);
+
+ this.addEvents({
+ beforeexpand : true,
+ expand: true,
+ beforecollapse: true,
+ collapse: true
+ });
+
+ Ext.grid.RowExpander.superclass.constructor.call(this);
+
+ if(this.tpl){
+ if(typeof this.tpl == 'string'){
+ this.tpl = new Ext.Template(this.tpl);
+ }
+ this.tpl.compile();
+ }
+
+ this.state = {};
+ this.bodyContent = {};
+};
+
+Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
+ header: "",
+ width: 20,
+ sortable: false,
+ fixed:true,
+ menuDisabled:true,
+ dataIndex: '',
+ id: 'expander',
+ lazyRender : true,
+ enableCaching: true,
+
+ getRowClass : function(record, rowIndex, p, ds){
+ p.cols = p.cols-1;
+ var content = this.bodyContent[record.id];
+ if(!content && !this.lazyRender){
+ content = this.getBodyContent(record, rowIndex);
+ }
+ if(content){
+ p.body = content;
+ }
+ return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
+ },
+
+ init : function(grid){
+ this.grid = grid;
+
+ var view = grid.getView();
+ view.getRowClass = this.getRowClass.createDelegate(this);
+
+ view.enableRowBody = true;
+
+ grid.on('render', function(){
+ view.mainBody.on('mousedown', this.onMouseDown, this);
+ }, this);
+ },
+
+ getBodyContent : function(record, index){
+ if(!this.enableCaching){
+ return this.tpl.apply(record.data);
+ }
+ var content = this.bodyContent[record.id];
+ if(!content){
+ content = this.tpl.apply(record.data);
+ this.bodyContent[record.id] = content;
+ }
+ return content;
+ },
+
+ onMouseDown : function(e, t){
+ if(t.className == 'x-grid3-row-expander'){
+ e.stopEvent();
+ var row = e.getTarget('.x-grid3-row');
+ this.toggleRow(row);
+ }
+ },
+
+ renderer : function(v, p, record){
+ p.cellAttr = 'rowspan="2"';
+ return '<div class="x-grid3-row-expander"> </div>';
+ },
+
+ beforeExpand : function(record, body, rowIndex){
+ if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){
+ if(this.tpl && this.lazyRender){
+ body.innerHTML = this.getBodyContent(record, rowIndex);
+ }
+ return true;
+ }else{
+ return false;
+ }
+ },
+
+ toggleRow : function(row){
+ if(typeof row == 'number'){
+ row = this.grid.view.getRow(row);
+ }
+ this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
+ },
+
+ expandRow : function(row){
+ if(typeof row == 'number'){
+ row = this.grid.view.getRow(row);
+ }
+ var record = this.grid.store.getAt(row.rowIndex);
+ var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
+ if(this.beforeExpand(record, body, row.rowIndex)){
+ this.state[record.id] = true;
+ Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
+ this.fireEvent('expand', this, record, body, row.rowIndex);
+ }
+ },
+
+ collapseRow : function(row){
+ if(typeof row == 'number'){
+ row = this.grid.view.getRow(row);
+ }
+ var record = this.grid.store.getAt(row.rowIndex);
+ var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
+ if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){
+ this.state[record.id] = false;
+ Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
+ this.fireEvent('collapse', this, record, body, row.rowIndex);
+ }
+ }
+});
activeTab:0,
autoScroll:true,
title: 'Configuration',
- items: [new tvheadend.dvb,
+ items: [new tvheadend.chconf,
+ new tvheadend.dvb,
new tvheadend.acleditor,
new tvheadend.cwceditor]
});