diff --git a/channels.c b/channels.c
index 07bb4c63..a4b3994c 100644
--- a/channels.c
+++ b/channels.c
@@ -41,8 +41,11 @@
struct th_channel_list channels;
struct th_transport_list all_transports;
int nchannels;
+int grouporder = 1000;
-struct th_channel_group_list all_channel_groups;
+struct th_channel_group_queue all_channel_groups;
+
+th_channel_group_t *defgroup;
void scanner_init(void);
@@ -63,33 +66,56 @@ channel_group_find(const char *name, int create)
{
th_channel_group_t *tcg;
- LIST_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
+ TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
if(!strcmp(name, tcg->tcg_name))
- break;
+ return tcg;
}
if(!create)
return NULL;
tcg = calloc(1, sizeof(th_channel_group_t));
tcg->tcg_name = strdup(name);
- LIST_INSERT_HEAD(&all_channel_groups, tcg, tcg_global_link);
+ tcg->tcg_tag = tag_get();
+
+ tcg->tcg_order = grouporder++;
+ TAILQ_INSERT_HEAD(&all_channel_groups, tcg, tcg_global_link);
return tcg;
}
+
+
+
+/**
+ *
+ */
+void
+channel_set_group(th_channel_t *ch, th_channel_group_t *tcg)
+{
+ if(ch->ch_group != NULL)
+ LIST_REMOVE(ch, ch_group_link);
+
+ ch->ch_group = tcg;
+ LIST_INSERT_SORTED(&tcg->tcg_channels, ch, ch_group_link, ch_order_cmp);
+}
+
/**
*
*/
void
-channel_set_group(th_channel_t *ch, const char *groupname)
+channel_group_destroy(th_channel_group_t *tcg)
{
- th_channel_group_t *tcg;
+ th_channel_t *ch;
- if(ch->ch_group != NULL)
- LIST_REMOVE(ch, ch_group_link);
+ if(defgroup == tcg)
+ return;
- tcg = channel_group_find(groupname, 1);
- ch->ch_group = tcg;
- LIST_INSERT_SORTED(&tcg->tcg_channels, ch, ch_group_link, ch_order_cmp);
+ while((ch = LIST_FIRST(&tcg->tcg_channels)) != NULL) {
+ channel_set_group(ch, defgroup);
+ }
+
+ TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
+ free((void *)tcg->tcg_name);
+ free(tcg);
}
/**
@@ -135,7 +161,7 @@ channel_find(const char *name, int create)
ch->ch_order = nchannels + 1000;
LIST_INSERT_SORTED(&channels, ch, ch_global_link, ch_order_cmp);
- channel_set_group(ch, "Default");
+ channel_set_group(ch, defgroup);
ch->ch_tag = tag_get();
nchannels++;
@@ -264,6 +290,11 @@ channels_load(void)
{
config_entry_t *ce;
+ TAILQ_INIT(&all_channel_groups);
+
+ defgroup = channel_group_find("Uncategorized", 1);
+ defgroup->tcg_cant_delete_me = 1;
+
TAILQ_FOREACH(ce, &config_list, ce_link) {
if(ce->ce_type == CFG_SUB && !strcasecmp("channel", ce->ce_key)) {
channel_load(&ce->ce_sub);
@@ -309,3 +340,20 @@ channel_by_tag(uint32_t tag)
return NULL;
}
+
+
+
+/**
+ *
+ */
+th_channel_group_t *
+channel_group_by_tag(uint32_t tag)
+{
+ th_channel_group_t *tcg;
+
+ TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link)
+ if(tcg->tcg_tag == tag)
+ return tcg;
+
+ return NULL;
+}
diff --git a/channels.h b/channels.h
index b8c3a7ef..97243e7e 100644
--- a/channels.h
+++ b/channels.h
@@ -33,4 +33,10 @@ void channel_unsubscribe(th_subscription_t *s);
th_channel_t *channel_find(const char *name, int create);
+th_channel_group_t *channel_group_find(const char *name, int create);
+
+th_channel_group_t *channel_group_by_tag(uint32_t tag);
+
+void channel_group_destroy(th_channel_group_t *tcg);
+
#endif /* CHANNELS_H */
diff --git a/htmlui.c b/htmlui.c
index eb74a435..b9a54da3 100644
--- a/htmlui.c
+++ b/htmlui.c
@@ -164,6 +164,11 @@ html_header(tcp_queue_t *tq, const char *title, int javascript, int width,
".content {padding-left: 3px; border-left: 1px solid #000000; "
"border-right: 1px solid #000000;}\r\n"
""
+ ".contentbig {padding-left: 3px; "
+ "border-left: 1px solid #000000; "
+ "border-right: 1px solid #000000; "
+ "font: 150% Verdana, Arial, Helvetica, sans-serif;}\r\n"
+ ""
".statuscont {float: left; margin: 4px; width: 400px}\r\n"
""
".logo {padding: 2px; width: 60px; height: 56px; "
@@ -261,6 +266,12 @@ top_menu(http_connection_t *hc, tcp_queue_t *tq)
if(html_verify_access(hc, "system-status"))
tcp_qprintf(tq, "
System Status");
+ if(html_verify_access(hc, "admin"))
+ tcp_qprintf(tq, "Manage channel groups");
+
+ if(html_verify_access(hc, "admin"))
+ tcp_qprintf(tq, "Manage channels");
+
tcp_qprintf(tq, "");
box_bottom(tq);
@@ -396,6 +407,7 @@ page_root(http_connection_t *hc, const char *remain, void *opaque)
int i;
int simple = is_client_simple(hc);
time_t firstend = INT32_MAX;
+ th_channel_group_t *tcg;
if(!html_verify_access(hc, "browse-events"))
return HTTP_STATUS_UNAUTHORIZED;
@@ -418,48 +430,59 @@ page_root(http_connection_t *hc, const char *remain, void *opaque)
html_header(&tq, "HTS/tvheadend", !simple, 700, i);
top_menu(hc, &tq);
- LIST_FOREACH(ch, &channels, ch_global_link) {
+ TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
+
box_top(&tq, "box");
- tcp_qprintf(&tq, "");
- if(!simple) {
- tcp_qprintf(&tq, "
");
- if(ch->ch_icon) {
- tcp_qprintf(&tq, "
"
- "
"
- "",
- ch->ch_tag,
- refstr_get(ch->ch_icon));
- }
- tcp_qprintf(&tq, "
");
- }
-
- tcp_qprintf(&tq, "
");
tcp_qprintf(&tq,
- "
"
- "%s",
- ch->ch_tag, ch->ch_name);
-
- if(tvheadend_streaming_host != NULL) {
- tcp_qprintf(&tq,
- "
Watch live",
- tvheadend_streaming_host, http_port, ch->ch_sname);
- } else {
- tcp_qprintf(&tq, "
");
- }
-
- e = epg_event_find_current_or_upcoming(ch);
-
- for(i = 0; i < 3 && e != NULL; i++) {
-
- output_event(hc, &tq, ch, e, simple);
- e = TAILQ_NEXT(e, e_link);
- }
-
- tcp_qprintf(&tq, "
");
+ "%s",
+ tcg->tcg_name);
box_bottom(&tq);
- tcp_qprintf(&tq, "
\r\n");
+ tcp_qprintf(&tq, "
");
+
+ LIST_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
+ box_top(&tq, "box");
+ tcp_qprintf(&tq, "");
+
+ if(!simple) {
+ tcp_qprintf(&tq, "
");
+ if(ch->ch_icon) {
+ tcp_qprintf(&tq, "
"
+ "
"
+ "",
+ ch->ch_tag,
+ refstr_get(ch->ch_icon));
+ }
+ tcp_qprintf(&tq, "
");
+ }
+
+ tcp_qprintf(&tq, "
");
+ tcp_qprintf(&tq,
+ "
"
+ "%s",
+ ch->ch_tag, ch->ch_name);
+
+ if(tvheadend_streaming_host != NULL) {
+ tcp_qprintf(&tq,
+ "
Watch live",
+ tvheadend_streaming_host, http_port, ch->ch_sname);
+ } else {
+ tcp_qprintf(&tq, "
");
+ }
+
+ e = epg_event_find_current_or_upcoming(ch);
+
+ for(i = 0; i < 3 && e != NULL; i++) {
+
+ output_event(hc, &tq, ch, e, simple);
+ e = TAILQ_NEXT(e, e_link);
+ }
+
+ tcp_qprintf(&tq, "
");
+ box_bottom(&tq);
+ tcp_qprintf(&tq, "
\r\n");
+ }
}
epg_unlock();
@@ -580,7 +603,7 @@ page_event(http_connection_t *hc, const char *remain, void *opaque)
struct tm a, b;
time_t stop;
char desc[4000];
- recop_t cmd = 0;
+ recop_t cmd = -1;
tv_pvr_status_t pvrstatus;
const char *pvr_txt, *pvr_color;
@@ -594,23 +617,26 @@ page_event(http_connection_t *hc, const char *remain, void *opaque)
return 404;
}
- remain = strchr(remain, '?');
- if(remain != NULL) {
+ if(http_arg_get(&hc->hc_url_args, "rec")) {
if(!html_verify_access(hc, "record-events")) {
epg_unlock();
return HTTP_STATUS_UNAUTHORIZED;
}
-
- remain++;
- if(!strncmp(remain, "rec=", 4))
- cmd = RECOP_ONCE;
- if(!strncmp(remain, "cancel=", 7))
- cmd = RECOP_CANCEL;
- pvr_event_record_op(e->e_ch, e, cmd);
-
+ cmd = RECOP_ONCE;
}
+ if(http_arg_get(&hc->hc_url_args, "cancel")) {
+ if(!html_verify_access(hc, "record-events")) {
+ epg_unlock();
+ return HTTP_STATUS_UNAUTHORIZED;
+ }
+ cmd = RECOP_CANCEL;
+ }
+
+ if(cmd != -1)
+ pvr_event_record_op(e->e_ch, e, cmd);
+
pvrstatus = pvr_prog_status(e);
localtime_r(&e->e_start, &a);
@@ -884,7 +910,7 @@ page_status(http_connection_t *hc, const char *remain, void *opaque)
box_top(&tq, "box");
- tcp_qprintf(&tq, "");
+ tcp_qprintf(&tq, "
");
tcp_qprintf(&tq, "
Input devices
");
tcp_qprintf(&tq, "");
box_bottom(&tq);
@@ -1020,7 +1046,7 @@ page_status(http_connection_t *hc, const char *remain, void *opaque)
tcp_qprintf(&tq, "
");
box_top(&tq, "box");
- tcp_qprintf(&tq, "
");
+ tcp_qprintf(&tq, "
");
tcp_qprintf(&tq, "
Active transports
");
tcp_qprintf(&tq, "");
box_bottom(&tq);
@@ -1123,7 +1149,7 @@ page_status(http_connection_t *hc, const char *remain, void *opaque)
tcp_qprintf(&tq, "
");
box_top(&tq, "box");
- tcp_qprintf(&tq, "
");
+ tcp_qprintf(&tq, "
");
tcp_qprintf(&tq, "
Subscriptions
");
tcp_qprintf(&tq, "");
box_bottom(&tq);
@@ -1191,6 +1217,91 @@ page_status(http_connection_t *hc, const char *remain, void *opaque)
}
+
+/**
+ * Manage channel groups
+ */
+static int
+page_chgroups(http_connection_t *hc, const char *remain, void *opaque)
+{
+ tcp_queue_t tq;
+ th_channel_group_t *tcg;
+ th_channel_t *ch;
+ int cnt;
+ const char *grp;
+ http_arg_t *ra;
+
+ if(!html_verify_access(hc, "admin"))
+ return HTTP_STATUS_UNAUTHORIZED;
+
+ if((grp = http_arg_get(&hc->hc_url_args, "newgrpname")) != NULL)
+ channel_group_find(grp, 1);
+
+ LIST_FOREACH(ra, &hc->hc_url_args, link) {
+ if(!strncmp(ra->key, "delgroup", 8))
+ break;
+ }
+
+ if(ra != NULL) {
+ tcg = channel_group_by_tag(atoi(ra->key + 8));
+ if(tcg != NULL) {
+ channel_group_destroy(tcg);
+ }
+ }
+
+ tcp_init_queue(&tq, -1);
+
+ html_header(&tq, "HTS/tvheadend", 0, 700, 0);
+ top_menu(hc, &tq);
+
+
+ tcp_qprintf(&tq, "
\r\n");
+
+
+ TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
+
+ tcp_qprintf(&tq, "
");
+ box_bottom(&tq);
+ tcp_qprintf(&tq, "
\r\n");
+
+ }
+
+
+ http_output_queue(hc, &tq, "text/html; charset=UTF-8");
+ return 0;
+}
+
+
+
+
/**
* HTML user interface setup code
*/
@@ -1202,4 +1313,5 @@ htmlui_start(void)
http_path_add("/channel", NULL, page_channel);
http_path_add("/pvrlog", NULL, page_pvrlog);
http_path_add("/status", NULL, page_status);
+ http_path_add("/chgrp", NULL, page_chgroups);
}
diff --git a/tvhead.h b/tvhead.h
index 5636b614..2aa15404 100644
--- a/tvhead.h
+++ b/tvhead.h
@@ -68,9 +68,8 @@ typedef struct dtimer {
*/
LIST_HEAD(th_subscription_list, th_subscription);
-TAILQ_HEAD(th_channel_queue, th_channel);
LIST_HEAD(th_channel_list, th_channel);
-LIST_HEAD(th_channel_group_list, th_channel_group);
+TAILQ_HEAD(th_channel_group_queue, th_channel_group);
LIST_HEAD(th_dvb_adapter_list, th_dvb_adapter);
LIST_HEAD(th_v4l_adapter_list, th_v4l_adapter);
LIST_HEAD(event_list, event);
@@ -91,6 +90,7 @@ extern time_t dispatch_clock;
extern int startupcounter;
extern struct th_transport_list all_transports;
extern struct th_channel_list channels;
+extern struct th_channel_group_queue all_channel_groups;
extern struct pvr_rec_list pvrr_global_list;
extern struct th_subscription_list subscriptions;
@@ -619,10 +619,13 @@ typedef struct tt_decoder {
* Channel groups
*/
typedef struct th_channel_group {
- LIST_ENTRY(th_channel_group) tcg_global_link;
+ TAILQ_ENTRY(th_channel_group) tcg_global_link;
const char *tcg_name;
struct th_channel_list tcg_channels;
+ int tcg_tag;
+ int tcg_order;
+ int tcg_cant_delete_me;
} th_channel_group_t;