Add channel configuration and setup.
htsclient.c is now no longer supported.
This commit is contained in:
parent
e7e4b9f710
commit
e544180acf
28 changed files with 850 additions and 800 deletions
2
Makefile
2
Makefile
|
@ -21,7 +21,7 @@ SRCS += iptv_input.c iptv_output.c
|
|||
|
||||
SRCS += avgen.c file_input.c
|
||||
|
||||
SRCS += htsclient.c rtsp.c rtp.c
|
||||
SRCS += rtsp.c rtp.c
|
||||
|
||||
SRCS += v4l.c
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ autorec_check_new_ar(autorec_t *ar)
|
|||
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;
|
||||
|
@ -306,7 +306,7 @@ autorec_load(void)
|
|||
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)
|
||||
|
|
2
avgen.c
2
avgen.c
|
@ -98,7 +98,7 @@ avgen_init(void)
|
|||
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));
|
||||
|
||||
|
|
216
channels.c
216
channels.c
|
@ -42,7 +42,8 @@
|
|||
#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)
|
||||
|
@ -83,6 +84,16 @@ channelcmp(const channel_t *a, const channel_t *b)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
chidcmp(const channel_t *a, const channel_t *b)
|
||||
{
|
||||
return a->ch_id - b->ch_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -115,38 +126,70 @@ channel_set_name(channel_t *ch, const char *name)
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static channel_t *
|
||||
channel_create(const char *name)
|
||||
{
|
||||
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));
|
||||
TAILQ_INIT(&ch->ch_epg_events);
|
||||
|
||||
channel_set_name(ch, name);
|
||||
|
||||
ch->ch_id = id;
|
||||
x = RB_INSERT_SORTED(&channel_identifier_tree, ch,
|
||||
ch_identifier_link, chidcmp);
|
||||
assert(x == NULL);
|
||||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
channel_t *
|
||||
channel_find(const char *name, int create)
|
||||
channel_find_by_name(const char *name, int create)
|
||||
{
|
||||
channel_t *ch, skel;
|
||||
|
||||
skel.ch_name = name;
|
||||
|
||||
if((ch = RB_FIND(&channel_tree, &skel, ch_global_link, channelcmp)) != NULL)
|
||||
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);
|
||||
}
|
||||
|
||||
if(create == 0)
|
||||
return NULL;
|
||||
|
||||
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();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
channel_t *
|
||||
channel_find_by_identifier(int id)
|
||||
{
|
||||
channel_t skel, *ch;
|
||||
skel.ch_id = id;
|
||||
ch = RB_FIND(&channel_identifier_tree, &skel, ch_identifier_link, chidcmp);
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static struct strtab commercial_detect_tab[] = {
|
||||
{ "none", COMMERCIAL_DETECT_NONE },
|
||||
{ "ttp192", COMMERCIAL_DETECT_TTP192 },
|
||||
|
@ -159,123 +202,11 @@ static struct strtab commercial_detect_tab[] = {
|
|||
void
|
||||
channels_load(void)
|
||||
{
|
||||
#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
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The index stuff should go away
|
||||
*/
|
||||
channel_t *
|
||||
channel_by_index(uint32_t index)
|
||||
{
|
||||
channel_t *ch;
|
||||
|
||||
RB_FOREACH(ch, &channel_tree, ch_global_link)
|
||||
if(ch->ch_index == index)
|
||||
return ch;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
channel_t *
|
||||
channel_by_tag(uint32_t tag)
|
||||
{
|
||||
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
|
||||
*/
|
||||
|
@ -283,7 +214,9 @@ static void
|
|||
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) ?: "?");
|
||||
|
@ -299,12 +232,15 @@ channel_rename(channel_t *ch, const char *newname)
|
|||
{
|
||||
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) {
|
||||
|
@ -326,6 +262,9 @@ channel_delete(channel_t *ch)
|
|||
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) {
|
||||
|
@ -344,11 +283,13 @@ channel_delete(channel_t *ch)
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -363,6 +304,9 @@ void
|
|||
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);
|
||||
|
|
10
channels.h
10
channels.h
|
@ -21,18 +21,12 @@
|
|||
|
||||
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);
|
||||
|
|
|
@ -156,18 +156,23 @@ dvb_transport_quality(th_transport_t *t)
|
|||
* 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;
|
||||
|
||||
tdmi = t->tht_dvb_mux_instance;
|
||||
return tdmi->tdmi_adapter->tda_displayname;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "\"%s\" on \"%s\"",
|
||||
tdmi->tdmi_network ?: "Unknown network",
|
||||
tdmi->tdmi_adapter->tda_rootpath);
|
||||
|
||||
return buf;
|
||||
/**
|
||||
* 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 tdmi->tdmi_network;
|
||||
}
|
||||
|
||||
|
||||
|
@ -209,7 +214,8 @@ dvb_transport_find(th_dvb_mux_instance_t *tdmi, uint16_t sid, int pmt_pid,
|
|||
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;
|
||||
|
||||
|
|
5
epg.c
5
epg.c
|
@ -26,7 +26,6 @@
|
|||
#include "channels.h"
|
||||
#include "epg.h"
|
||||
#include "dispatch.h"
|
||||
#include "htsclient.h"
|
||||
#include "htsp.h"
|
||||
#include "autorec.h"
|
||||
|
||||
|
@ -413,7 +412,7 @@ epg_channel_maintain(void *aux, int64_t clk)
|
|||
|
||||
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 */
|
||||
|
||||
|
@ -559,7 +558,7 @@ epg_search(struct event_list *h, const char *title,
|
|||
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;
|
||||
|
||||
|
|
|
@ -250,7 +250,7 @@ xmltv_resolve_by_events(xmltv_channel_t *xc)
|
|||
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;
|
||||
|
||||
|
@ -289,14 +289,14 @@ xmltv_transfer_events(xmltv_grabber_t *xg)
|
|||
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);
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include "tvhead.h"
|
||||
#include "channels.h"
|
||||
#include "subscriptions.h"
|
||||
#include "htsclient.h"
|
||||
#include "ffmuxer.h"
|
||||
#include "buffer.h"
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ file_input_init(void)
|
|||
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);
|
||||
|
|
575
htsclient.c
575
htsclient.c
|
@ -1,575 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
26
htsclient.h
26
htsclient.h
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* 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_ */
|
13
htsp.c
13
htsp.c
|
@ -80,7 +80,7 @@ htsp_build_channel_msg(channel_t *ch, const char *method)
|
|||
|
||||
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);
|
||||
|
||||
|
@ -102,7 +102,7 @@ htsp_send_all_channels(htsp_t *htsp)
|
|||
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;
|
||||
|
||||
|
@ -140,13 +140,12 @@ htsp_subscribe(rpc_session_t *ses, htsmsg_t *in, void *opaque)
|
|||
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) {
|
||||
|
|
|
@ -51,7 +51,7 @@ htsp_packet_input(void *opaque, th_muxstream_t *tms, th_pkt_t *pkt)
|
|||
*/
|
||||
|
||||
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);
|
||||
|
@ -89,7 +89,7 @@ htsp_subscription_callback(struct th_subscription *s,
|
|||
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++;
|
||||
|
@ -113,7 +113,7 @@ htsp_subscription_callback(struct th_subscription *s,
|
|||
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);
|
||||
|
@ -159,7 +159,7 @@ htsp_muxer_unsubscribe(htsp_t *htsp, uint32_t id)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ output_multicast_load(struct config_head *head)
|
|||
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));
|
||||
|
||||
|
|
2
main.c
2
main.c
|
@ -40,7 +40,6 @@
|
|||
#include "dvb/dvb.h"
|
||||
#include "v4l.h"
|
||||
#include "channels.h"
|
||||
#include "htsclient.h"
|
||||
#include "epg.h"
|
||||
#include "epg_xmltv.h"
|
||||
#include "pvr.h"
|
||||
|
@ -309,7 +308,6 @@ main(int argc, char **argv)
|
|||
|
||||
pvr_init();
|
||||
output_multicast_setup();
|
||||
client_start();
|
||||
|
||||
p = atoi(config_get_str("http-server-port", "9981"));
|
||||
if(p)
|
||||
|
|
2
psi.c
2
psi.c
|
@ -618,7 +618,7 @@ psi_load_transport_settings(htsmsg_t *m, th_transport_t *t)
|
|||
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))
|
||||
|
|
3
pvr.c
3
pvr.c
|
@ -37,7 +37,6 @@
|
|||
#include "tvhead.h"
|
||||
#include "channels.h"
|
||||
#include "subscriptions.h"
|
||||
#include "htsclient.h"
|
||||
#include "pvr.h"
|
||||
#include "epg.h"
|
||||
#include "dispatch.h"
|
||||
|
@ -438,7 +437,7 @@ pvr_database_load(void)
|
|||
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;
|
||||
|
|
6
rpc.c
6
rpc.c
|
@ -51,7 +51,7 @@ rpc_build_channel_msg(channel_t *ch)
|
|||
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;
|
||||
|
@ -148,7 +148,7 @@ rpc_channels_list(rpc_session_t *ses, htsmsg_t *in, void *opaque)
|
|||
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;
|
||||
|
@ -174,7 +174,7 @@ rpc_event_info(rpc_session_t *ses, htsmsg_t *in, void *opaque)
|
|||
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) {
|
||||
|
|
2
rtsp.c
2
rtsp.c
|
@ -104,7 +104,7 @@ rtsp_channel_by_url(char *url)
|
|||
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;
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ sp_packet_input(void *opaque, th_muxstream_t *tms, th_pkt_t *pkt)
|
|||
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);
|
||||
|
|
|
@ -558,7 +558,7 @@ transport_map_channel(th_transport_t *t, channel_t *ch)
|
|||
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);
|
||||
|
|
17
tvhead.h
17
tvhead.h
|
@ -95,7 +95,7 @@ LIST_HEAD(autorec_list, autorec);
|
|||
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;
|
||||
|
||||
|
@ -439,6 +439,8 @@ typedef struct th_transport {
|
|||
|
||||
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);
|
||||
|
@ -778,22 +780,19 @@ typedef struct tt_decoder {
|
|||
*/
|
||||
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,
|
||||
|
|
221
webui/extjs.c
221
webui/extjs.c
|
@ -32,6 +32,8 @@
|
|||
#include "webui.h"
|
||||
#include "access.h"
|
||||
#include "dtable.h"
|
||||
#include "channels.h"
|
||||
#include "psi.h"
|
||||
|
||||
#include "dvb/dvb.h"
|
||||
#include "dvb/dvb_support.h"
|
||||
|
@ -105,6 +107,7 @@ extjs_root(http_connection_t *hc, http_reply_t *hr,
|
|||
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
|
||||
|
@ -199,6 +202,38 @@ extjs_tablemgr(http_connection_t *hc, http_reply_t *hr,
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -328,7 +363,7 @@ json_single_record(htsmsg_t *rec, const char *root)
|
|||
array = htsmsg_create_array();
|
||||
|
||||
htsmsg_add_msg(array, NULL, rec);
|
||||
htsmsg_add_msg(out, "dvbadapters", array);
|
||||
htsmsg_add_msg(out, root, array);
|
||||
return out;
|
||||
|
||||
}
|
||||
|
@ -399,6 +434,188 @@ extjs_dvbadapter(http_connection_t *hc, http_reply_t *hr,
|
|||
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
|
||||
*/
|
||||
|
@ -410,4 +627,6 @@ extjs_start(void)
|
|||
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);
|
||||
}
|
||||
|
|
361
webui/static/app/chconf.js
Normal file
361
webui/static/app/chconf.js
Normal file
|
@ -0,0 +1,361 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
|
@ -167,6 +167,7 @@ tvheadend.dvb_adapterdetails = function(adapterId, adapterName, treenode) {
|
|||
border: false,
|
||||
items:[status, confpanel],
|
||||
tbar:[{
|
||||
iconCls:'add',
|
||||
text: 'Add mux(es)',
|
||||
handler: addmux
|
||||
},{
|
||||
|
|
|
@ -143,3 +143,135 @@ Ext.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -87,7 +87,8 @@ tvheadend.app = function() {
|
|||
activeTab:0,
|
||||
autoScroll:true,
|
||||
title: 'Configuration',
|
||||
items: [new tvheadend.dvb,
|
||||
items: [new tvheadend.chconf,
|
||||
new tvheadend.dvb,
|
||||
new tvheadend.acleditor,
|
||||
new tvheadend.cwceditor]
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue