Add HTML/web interface
This commit is contained in:
parent
0c7216354e
commit
7cbd3d88aa
3 changed files with 780 additions and 1 deletions
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@
|
|||
SRCS = main.c dispatch.c channels.c transports.c teletext.c psi.c \
|
||||
subscriptions.c tsmux.c tsdemux.c pes.c buffer.c tcp.c
|
||||
|
||||
SRCS += http.c
|
||||
SRCS += http.c htmlui.c
|
||||
|
||||
SRCS += pvr.c
|
||||
|
||||
|
|
753
htmlui.c
Normal file
753
htmlui.c
Normal file
|
@ -0,0 +1,753 @@
|
|||
/*
|
||||
* tvheadend, HTML user 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 <pthread.h>
|
||||
#include <assert.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 "htmlui.h"
|
||||
#include "channels.h"
|
||||
#include "epg.h"
|
||||
#include "pvr.h"
|
||||
#include "strtab.h"
|
||||
|
||||
static struct strtab recstatustxt[] = {
|
||||
{ "Scheduled", HTSTV_PVR_STATUS_SCHEDULED },
|
||||
{ "Recording", HTSTV_PVR_STATUS_RECORDING },
|
||||
{ "Done", HTSTV_PVR_STATUS_DONE },
|
||||
|
||||
{ "Recording aborted", HTSTV_PVR_STATUS_ABORTED },
|
||||
|
||||
{ "No transponder", HTSTV_PVR_STATUS_NO_TRANSPONDER },
|
||||
{ "File error", HTSTV_PVR_STATUS_FILE_ERROR },
|
||||
{ "Disk full", HTSTV_PVR_STATUS_DISK_FULL },
|
||||
{ "Buffer error", HTSTV_PVR_STATUS_BUFFER_ERROR },
|
||||
};
|
||||
|
||||
static struct strtab recstatuscolor[] = {
|
||||
{ "#3333aa", HTSTV_PVR_STATUS_SCHEDULED },
|
||||
{ "#aa3333", HTSTV_PVR_STATUS_RECORDING },
|
||||
{ "#33aa33", HTSTV_PVR_STATUS_DONE },
|
||||
{ "#aa3333", HTSTV_PVR_STATUS_ABORTED },
|
||||
{ "#aa3333", HTSTV_PVR_STATUS_NO_TRANSPONDER },
|
||||
{ "#aa3333", HTSTV_PVR_STATUS_FILE_ERROR },
|
||||
{ "#aa3333", HTSTV_PVR_STATUS_DISK_FULL },
|
||||
{ "#aa3333", HTSTV_PVR_STATUS_BUFFER_ERROR },
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
pvrstatus_to_html(tv_pvr_status_t pvrstatus, const char **text,
|
||||
const char **col)
|
||||
{
|
||||
*text = val2str(pvrstatus, recstatustxt);
|
||||
if(*text == NULL)
|
||||
return -1;
|
||||
|
||||
*col = val2str(pvrstatus, recstatuscolor);
|
||||
if(*col == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
html_header(tcp_queue_t *tq, const char *title, int javascript)
|
||||
{
|
||||
tcp_qprintf(tq,
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
|
||||
"http://www.w3.org/TR/html4/strict.dtd\">\r\n"
|
||||
"<html><head>\r\n"
|
||||
"<title>%s</title>\r\n"
|
||||
"<meta http-equiv=\"Content-Type\" "
|
||||
"content=\"text/html; charset=utf-8\">\r\n", title);
|
||||
|
||||
tcp_qprintf(tq,
|
||||
"<style type=\"text/css\">\r\n"
|
||||
"<!--\r\n"
|
||||
"img { border: 0px; }\r\n"
|
||||
"a:link { text-decoration: none}\r\n"
|
||||
"a:visited { text-decoration: none}\r\n"
|
||||
"a:active { text-decoration: none}\r\n"
|
||||
"a:link { text-decoration: none; color: #000000}\r\n"
|
||||
"a:visited { text-decoration: none; color: #000000}\r\n"
|
||||
"a:active { text-decoration: none; color: #000000}\r\n"
|
||||
"a:hover { text-decoration: underline; color: CC3333}\r\n"
|
||||
""
|
||||
"body {margin: 4px 4px; "
|
||||
"font: 75% Verdana, Arial, Helvetica, sans-serif; "
|
||||
"width: 600px; margin-right: auto; margin-left: auto;}\r\n"
|
||||
""
|
||||
"#box {background: #cccc99;}\r\n"
|
||||
".roundtop {background: #ffffff;}\r\n"
|
||||
".roundbottom {background: #ffffff;}\r\n"
|
||||
".r1{margin: 0 5px; height: 1px; overflow: hidden; "
|
||||
"background: #000000; border-left: 1px solid #000000; "
|
||||
"border-right: 1px solid #000000;}\r\n"
|
||||
""
|
||||
".r2{margin: 0 3px; height: 1px; overflow: hidden; "
|
||||
"background: #cccc99; border-left: 1px solid #000000; "
|
||||
"border-right: 1px solid #000000; border-width: 0 2px;}\r\n"
|
||||
""
|
||||
".r3{margin: 0 2px; height: 1px; overflow: hidden; "
|
||||
"background: #cccc99; border-left: 1px solid #000000; "
|
||||
"border-right: 1px solid #000000;}\r\n"
|
||||
""
|
||||
".r4{margin: 0 1px; height: 2px; overflow: hidden; "
|
||||
"background: #cccc99; border-left: 1px solid #000000; "
|
||||
"border-right: 1px solid #000000;}\r\n"
|
||||
""
|
||||
".content3 {padding-left: 3px; height: 60px; "
|
||||
"border-left: 1px solid #000000; "
|
||||
"border-right: 1px solid #000000;}\r\n"
|
||||
""
|
||||
".content {padding-left: 3px; border-left: 1px solid #000000; "
|
||||
"border-right: 1px solid #000000;}\r\n"
|
||||
""
|
||||
".logo {padding: 2px; width: 60px; height: 56px; "
|
||||
"float: left};\r\n"
|
||||
""
|
||||
".over {float: left}\r\n"
|
||||
".toptxt {float: left; width: 165px; text-align: center}\r\n"
|
||||
""
|
||||
"#meny {margin: 0; padding: 0}\r\n"
|
||||
"#meny li{display: inline; list-style-type: none;}\r\n"
|
||||
"#meny a{padding: 1.15em 0.8em; text-decoration: none;}\r\n"
|
||||
"-->\r\n"
|
||||
"</style>");
|
||||
|
||||
if(javascript) {
|
||||
tcp_qprintf(tq,
|
||||
"<script type=\"text/javascript\" "
|
||||
"src=\"http://www.olebyn.nu/hts/overlib.js\"></script>\r\n");
|
||||
|
||||
|
||||
|
||||
tcp_qprintf(tq,
|
||||
"<script language=\"javascript\">\r\n"
|
||||
"<!-- Begin\r\n"
|
||||
"function epop() {\r\n"
|
||||
" props=window.open(epop.arguments[0],"
|
||||
"'poppage', 'toolbars=0, scrollbars=0, location=0, "
|
||||
"statusbars=0, menubars=0, resizable=0, "
|
||||
"width=600, height=300 left = 100, top = 100');\r\n}\r\n"
|
||||
"// End -->\r\n"
|
||||
"</script>\r\n");
|
||||
}
|
||||
/* BODY start */
|
||||
|
||||
tcp_qprintf(tq, "</head><body>\r\n");
|
||||
|
||||
if(javascript)
|
||||
tcp_qprintf(tq,
|
||||
"<div id=\"overDiv\" style=\"position:absolute; "
|
||||
"visibility:hidden; z-index:1000;\"></div>\r\n");
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
html_footer(tcp_queue_t *tq)
|
||||
{
|
||||
tcp_qprintf(tq,
|
||||
"</body></html>\r\n\r\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
box_top(tcp_queue_t *tq, const char *style)
|
||||
{
|
||||
tcp_qprintf(tq, "<div id=\"%s\">"
|
||||
"<div class=\"roundtop\">"
|
||||
"<div class=\"r1\"></div>"
|
||||
"<div class=\"r2\"></div>"
|
||||
"<div class=\"r3\"></div>"
|
||||
"<div class=\"r4\"></div>"
|
||||
"</div>", style);
|
||||
}
|
||||
|
||||
static void
|
||||
box_bottom(tcp_queue_t *tq)
|
||||
{
|
||||
tcp_qprintf(tq,
|
||||
"<div class=\"roundbottom\">"
|
||||
"<div class=\"r4\"></div>"
|
||||
"<div class=\"r3\"></div>"
|
||||
"<div class=\"r2\"></div>"
|
||||
"<div class=\"r1\"></div>"
|
||||
"</div></div>");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
top_menu(tcp_queue_t *tq)
|
||||
{
|
||||
box_top(tq, "box");
|
||||
|
||||
tcp_qprintf(tq,
|
||||
"<div class=\"content\">"
|
||||
"<ul id=\"meny\">"
|
||||
"<li><a href=\"/\">TV Guide</a></li>"
|
||||
"<li><a href=\"/pvrlog\">Recordings</a></li>"
|
||||
"<li><a href=\"/status\">System Status</a></li>"
|
||||
"</ul></div>");
|
||||
|
||||
box_bottom(tq);
|
||||
tcp_qprintf(tq, "<br>\r\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
esacpe_char(char *dst, int dstlen, const char *src, char c,
|
||||
const char *repl)
|
||||
{
|
||||
char v;
|
||||
const char *r;
|
||||
|
||||
while((v = *src++) && dstlen > 1) {
|
||||
if(v != c) {
|
||||
*dst++ = v;
|
||||
dstlen--;
|
||||
} else {
|
||||
r = repl;
|
||||
while(dstlen > 1 && *r) {
|
||||
*dst++ = *r++;
|
||||
dstlen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
*dst = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
is_client_simple(http_connection_t *hc)
|
||||
{
|
||||
char *c;
|
||||
|
||||
if((c = http_arg_get(&hc->hc_args, "UA-OS")) != NULL) {
|
||||
if(strstr(c, "Windows CE") || strstr(c, "Pocket PC"))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if((c = http_arg_get(&hc->hc_args, "x-wap-profile")) != NULL) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Output_event
|
||||
*/
|
||||
|
||||
static void
|
||||
output_event(http_connection_t *hc, tcp_queue_t *tq, th_channel_t *ch,
|
||||
event_t *e, int simple)
|
||||
{
|
||||
char title[100];
|
||||
char bufa[4000];
|
||||
char overlibstuff[4000];
|
||||
struct tm a, b;
|
||||
time_t stop;
|
||||
event_t *cur;
|
||||
tv_pvr_status_t pvrstatus;
|
||||
const char *pvr_txt, *pvr_color;
|
||||
|
||||
localtime_r(&e->e_start, &a);
|
||||
stop = e->e_start + e->e_duration;
|
||||
localtime_r(&stop, &b);
|
||||
|
||||
pvrstatus = pvr_prog_status(e);
|
||||
|
||||
cur = epg_event_get_current(ch);
|
||||
|
||||
if(!simple && e->e_desc != NULL) {
|
||||
esacpe_char(bufa, sizeof(bufa), e->e_desc, '\'', "");
|
||||
|
||||
snprintf(overlibstuff, sizeof(overlibstuff),
|
||||
"onmouseover=\"return overlib('%s')\" "
|
||||
"onmouseout=\"return nd();\"",
|
||||
bufa);
|
||||
} else {
|
||||
overlibstuff[0] = 0;
|
||||
}
|
||||
|
||||
if(1 || simple) {
|
||||
snprintf(bufa, sizeof(bufa),
|
||||
"/event/%d", e->e_tag);
|
||||
} else {
|
||||
snprintf(bufa, sizeof(bufa),
|
||||
"javascript:epop('/event/%d')", e->e_tag);
|
||||
}
|
||||
|
||||
esacpe_char(title, sizeof(title), e->e_title, '"', "'");
|
||||
|
||||
tcp_qprintf(tq,
|
||||
"<div>"
|
||||
"<a href=\"%s\" %s>"
|
||||
"<span style=\"width: %dpx;float: left%s\">"
|
||||
"%02d:%02d - %02d:%02d</span>"
|
||||
"<span style=\"width: %dpx; float: left%s\">%s</span></a>",
|
||||
bufa,
|
||||
overlibstuff,
|
||||
simple ? 80 : 100,
|
||||
e == cur ? ";font-weight:bold" : "",
|
||||
a.tm_hour, a.tm_min, b.tm_hour, b.tm_min,
|
||||
simple ? 100 : 250,
|
||||
e == cur ? ";font-weight:bold" : "",
|
||||
title
|
||||
);
|
||||
|
||||
if(!pvrstatus_to_html(pvrstatus, &pvr_txt, &pvr_color)) {
|
||||
tcp_qprintf(tq,
|
||||
"<span style=\"font-style:italic;color:%s;font-weight:bold\">"
|
||||
"%s</span></div><br>",
|
||||
pvr_color, pvr_txt);
|
||||
} else {
|
||||
tcp_qprintf(tq, "</div><br>");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Root page
|
||||
*/
|
||||
static int
|
||||
page_root(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
tcp_queue_t tq;
|
||||
th_channel_t *ch;
|
||||
event_t *e;
|
||||
int i;
|
||||
int simple = is_client_simple(hc);
|
||||
|
||||
|
||||
tcp_init_queue(&tq, -1);
|
||||
|
||||
html_header(&tq, "HTS/tvheadend", !simple);
|
||||
|
||||
top_menu(&tq);
|
||||
|
||||
epg_lock();
|
||||
TAILQ_FOREACH(ch, &channels, ch_global_link) {
|
||||
box_top(&tq, "box");
|
||||
tcp_qprintf(&tq, "<div class=\"content3\">");
|
||||
|
||||
if(!simple) {
|
||||
tcp_qprintf(&tq, "<div class=\"logo\">");
|
||||
if(ch->ch_icon) {
|
||||
tcp_qprintf(&tq, "<a href=\"channel/%d\">"
|
||||
"<img src=\"%s\" height=56px>"
|
||||
"</a>",
|
||||
ch->ch_tag,
|
||||
refstr_get(ch->ch_icon));
|
||||
}
|
||||
tcp_qprintf(&tq, "</div>");
|
||||
}
|
||||
|
||||
tcp_qprintf(&tq, "<div class=\"over\">");
|
||||
tcp_qprintf(&tq, "<strong><a href=\"channel/%d\">%s</a></strong><br>",
|
||||
ch->ch_tag, ch->ch_name);
|
||||
|
||||
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, "</div></div>");
|
||||
box_bottom(&tq);
|
||||
tcp_qprintf(&tq, "<br>\r\n");
|
||||
}
|
||||
epg_unlock();
|
||||
|
||||
html_footer(&tq);
|
||||
http_output_queue(hc, &tq, "text/html; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Channel page
|
||||
*/
|
||||
|
||||
|
||||
const char *days[7] = {
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
page_channel(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
tcp_queue_t tq;
|
||||
th_channel_t *ch = NULL;
|
||||
event_t *e = NULL;
|
||||
int i;
|
||||
int simple = is_client_simple(hc);
|
||||
int channeltag = -1;
|
||||
int w, doff = 0, wday;
|
||||
struct tm a;
|
||||
|
||||
i = sscanf(remain, "%d/%d", &channeltag, &doff);
|
||||
ch = channel_by_tag(channeltag);
|
||||
if(i != 2)
|
||||
doff = 0;
|
||||
|
||||
if(ch == NULL) {
|
||||
http_error(hc, 404);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tcp_init_queue(&tq, -1);
|
||||
|
||||
html_header(&tq, "HTS/tvheadend", !simple);
|
||||
|
||||
top_menu(&tq);
|
||||
|
||||
epg_lock();
|
||||
|
||||
box_top(&tq, "box");
|
||||
|
||||
tcp_qprintf(&tq, "<div class=\"content\">");
|
||||
tcp_qprintf(&tq, "<strong><a href=\"channel/%d\">%s</a></strong><br>",
|
||||
ch->ch_tag, ch->ch_name);
|
||||
|
||||
e = epg_event_find_current_or_upcoming(ch);
|
||||
if(e != NULL) {
|
||||
localtime_r(&e->e_start, &a);
|
||||
wday = a.tm_wday;
|
||||
|
||||
|
||||
for(w = 0; w < 7; w++) {
|
||||
|
||||
tcp_qprintf(&tq,
|
||||
"<a href=\"/channel/%d/%d\""
|
||||
"<u><i>%s</i></u></a><br>",
|
||||
ch->ch_tag, w,
|
||||
days[(wday + w) % 7]);
|
||||
|
||||
while(e != NULL) {
|
||||
localtime_r(&e->e_start, &a);
|
||||
if(a.tm_wday != wday + w)
|
||||
break;
|
||||
|
||||
if(a.tm_wday == wday + doff)
|
||||
output_event(hc, &tq, ch, e, simple);
|
||||
e = TAILQ_NEXT(e, e_link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tcp_qprintf(&tq, "</div>");
|
||||
box_bottom(&tq);
|
||||
tcp_qprintf(&tq, "<br>\r\n");
|
||||
epg_unlock();
|
||||
|
||||
html_footer(&tq);
|
||||
http_output_queue(hc, &tq, "text/html; charset=UTF-8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event page
|
||||
*/
|
||||
static int
|
||||
page_event(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
tcp_queue_t tq;
|
||||
int simple = is_client_simple(hc);
|
||||
int eventid = atoi(remain);
|
||||
char title[100];
|
||||
event_t *e;
|
||||
struct tm a, b;
|
||||
time_t stop;
|
||||
char desc[4000];
|
||||
recop_t cmd = 0;
|
||||
tv_pvr_status_t pvrstatus;
|
||||
const char *pvr_txt, *pvr_color;
|
||||
|
||||
epg_lock();
|
||||
e = epg_event_find_by_tag(eventid);
|
||||
if(e == NULL) {
|
||||
epg_unlock();
|
||||
return 404;
|
||||
}
|
||||
|
||||
remain = strchr(remain, '?');
|
||||
if(remain != NULL) {
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
pvrstatus = pvr_prog_status(e);
|
||||
|
||||
localtime_r(&e->e_start, &a);
|
||||
stop = e->e_start + e->e_duration;
|
||||
localtime_r(&stop, &b);
|
||||
|
||||
tcp_init_queue(&tq, -1);
|
||||
|
||||
html_header(&tq, "HTS/tvheadend", 0);
|
||||
top_menu(&tq);
|
||||
|
||||
tcp_qprintf(&tq, "<form method=\"get\" action=\"/event/%d\">", eventid);
|
||||
|
||||
|
||||
box_top(&tq, "box");
|
||||
|
||||
tcp_qprintf(&tq, "<div class=\"content\">");
|
||||
|
||||
|
||||
esacpe_char(title, sizeof(title), e->e_title, '"', "'");
|
||||
esacpe_char(desc, sizeof(desc), e->e_desc, '\'', "");
|
||||
|
||||
tcp_qprintf(&tq,
|
||||
"<div style=\"width: %dpx;float: left;font-weight:bold\">"
|
||||
"%02d:%02d - %02d:%02d</div>"
|
||||
"<div style=\"width: %dpx; float: left;font-weight:bold\">"
|
||||
"%s</div></a>",
|
||||
simple ? 80 : 100,
|
||||
a.tm_hour, a.tm_min, b.tm_hour, b.tm_min,
|
||||
simple ? 100 : 250,
|
||||
title);
|
||||
|
||||
if(!pvrstatus_to_html(pvrstatus, &pvr_txt, &pvr_color))
|
||||
tcp_qprintf(&tq,
|
||||
"<div style=\"font-style:italic;color:%s;font-weight:bold\">"
|
||||
"%s</div>",
|
||||
pvr_color, pvr_txt);
|
||||
else
|
||||
tcp_qprintf(&tq, "<br>");
|
||||
|
||||
|
||||
tcp_qprintf(&tq, "<br>%s", desc);
|
||||
|
||||
tcp_qprintf(&tq,"<div style=\"text-align: center\">");
|
||||
|
||||
switch(pvrstatus) {
|
||||
case HTSTV_PVR_STATUS_SCHEDULED:
|
||||
case HTSTV_PVR_STATUS_RECORDING:
|
||||
tcp_qprintf(&tq,
|
||||
"<input type=\"submit\" name=\"cancel\" "
|
||||
"value=\"Cancel recording\">");
|
||||
break;
|
||||
|
||||
|
||||
case HTSTV_PVR_STATUS_NONE:
|
||||
tcp_qprintf(&tq,
|
||||
"<input type=\"submit\" name=\"rec\" "
|
||||
"value=\"Record\">");
|
||||
break;
|
||||
|
||||
default:
|
||||
tcp_qprintf(&tq,
|
||||
"<input type=\"submit\" name=\"cancel\" "
|
||||
"value=\"Clear error status\">");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
tcp_qprintf(&tq, "</div></div></div>");
|
||||
box_bottom(&tq);
|
||||
tcp_qprintf(&tq, "</form>\r\n");
|
||||
html_footer(&tq);
|
||||
http_output_queue(hc, &tq, "text/html; charset=UTF-8");
|
||||
|
||||
epg_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
pvrcmp(const void *A, const void *B)
|
||||
{
|
||||
const pvr_rec_t *a = *(const pvr_rec_t **)A;
|
||||
const pvr_rec_t *b = *(const pvr_rec_t **)B;
|
||||
|
||||
return a->pvrr_start - b->pvrr_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event page
|
||||
*/
|
||||
static int
|
||||
page_pvrlog(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
tcp_queue_t tq;
|
||||
int simple = is_client_simple(hc);
|
||||
pvr_rec_t *pvrr;
|
||||
event_t *e;
|
||||
char escapebuf[4000], href[4000];
|
||||
char title[100];
|
||||
char channel[100];
|
||||
struct tm a, b, day;
|
||||
const char *pvr_txt, *pvr_color;
|
||||
int c, i;
|
||||
pvr_rec_t **pv;
|
||||
|
||||
tcp_init_queue(&tq, -1);
|
||||
html_header(&tq, "HTS/tvheadend", 0);
|
||||
top_menu(&tq);
|
||||
|
||||
box_top(&tq, "box");
|
||||
|
||||
epg_lock();
|
||||
|
||||
tcp_qprintf(&tq, "<div class=\"content\">");
|
||||
|
||||
c = 0;
|
||||
LIST_FOREACH(pvrr, &pvrr_global_list, pvrr_global_link)
|
||||
c++;
|
||||
|
||||
pv = alloca(c * sizeof(pvr_rec_t *));
|
||||
|
||||
i = 0;
|
||||
LIST_FOREACH(pvrr, &pvrr_global_list, pvrr_global_link)
|
||||
pv[i++] = pvrr;
|
||||
|
||||
|
||||
qsort(pv, i, sizeof(pvr_rec_t *), pvrcmp);
|
||||
|
||||
memset(&day, -1, sizeof(struct tm));
|
||||
|
||||
for(i = 0; i < c; i++) {
|
||||
pvrr = pv[i];
|
||||
|
||||
e = epg_event_find_by_time(pvrr->pvrr_channel, pvrr->pvrr_start);
|
||||
|
||||
if(e != NULL && !simple && e->e_desc != NULL) {
|
||||
esacpe_char(escapebuf, sizeof(escapebuf), e->e_desc, '\'', "");
|
||||
|
||||
snprintf(href, sizeof(href),
|
||||
"<a href=\"/event/%d\" onmouseover=\"return overlib('%s')\" "
|
||||
"onmouseout=\"return nd();\">",
|
||||
e->e_tag,
|
||||
escapebuf);
|
||||
|
||||
} else {
|
||||
href[0] = 0;
|
||||
}
|
||||
|
||||
esacpe_char(channel, sizeof(channel),
|
||||
pvrr->pvrr_channel->ch_name, '"', "'");
|
||||
|
||||
esacpe_char(title, sizeof(title),
|
||||
pvrr->pvrr_title ?: "Unnamed recording", '"', "'");
|
||||
|
||||
localtime_r(&pvrr->pvrr_start, &a);
|
||||
localtime_r(&pvrr->pvrr_stop, &b);
|
||||
|
||||
if(a.tm_wday != day.tm_wday ||
|
||||
a.tm_mday != day.tm_mday ||
|
||||
a.tm_mon != day.tm_mon ||
|
||||
a.tm_year != day.tm_year) {
|
||||
|
||||
memcpy(&day, &a, sizeof(struct tm));
|
||||
|
||||
tcp_qprintf(&tq,
|
||||
"<br><b><i>%s, %d/%d</i></b><br>",
|
||||
days[day.tm_wday],
|
||||
day.tm_mday,
|
||||
day.tm_mon + 1);
|
||||
}
|
||||
|
||||
|
||||
tcp_qprintf(&tq,
|
||||
"%s"
|
||||
"<div style=\"width: %dpx;float: left\">"
|
||||
"%s</div>"
|
||||
"<div style=\"width: %dpx;float: left\">"
|
||||
"%02d:%02d - %02d:%02d</div>"
|
||||
"<div style=\"width: %dpx; float: left\">%s</div>%s",
|
||||
href,
|
||||
simple ? 80 : 100,
|
||||
channel,
|
||||
simple ? 80 : 100,
|
||||
a.tm_hour, a.tm_min, b.tm_hour, b.tm_min,
|
||||
simple ? 100 : 250,
|
||||
title,
|
||||
href[0] ? "</a>" : "");
|
||||
|
||||
if(!pvrstatus_to_html(pvrr->pvrr_status, &pvr_txt, &pvr_color)) {
|
||||
tcp_qprintf(&tq,
|
||||
"<div style=\"font-style:italic;color:%s;font-weight:bold\">"
|
||||
"%s</div>",
|
||||
pvr_color, pvr_txt);
|
||||
} else {
|
||||
tcp_qprintf(&tq, "<br>");
|
||||
}
|
||||
}
|
||||
|
||||
epg_unlock();
|
||||
tcp_qprintf(&tq, "<br></div>\r\n");
|
||||
|
||||
box_bottom(&tq);
|
||||
tcp_qprintf(&tq, "</form>\r\n");
|
||||
html_footer(&tq);
|
||||
http_output_queue(hc, &tq, "text/html; charset=UTF-8");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML user interface setup code
|
||||
*/
|
||||
void
|
||||
htmlui_start(void)
|
||||
{
|
||||
http_path_add("/", NULL, page_root);
|
||||
http_path_add("/event", NULL, page_event);
|
||||
http_path_add("/channel", NULL, page_channel);
|
||||
http_path_add("/pvrlog", NULL, page_pvrlog);
|
||||
}
|
26
htmlui.h
Normal file
26
htmlui.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* tvheadend, HTML user 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 <htmlui://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HTMLUI_H_
|
||||
#define HTMLUI_H_
|
||||
|
||||
#include "http.h"
|
||||
|
||||
void htmlui_start(void);
|
||||
|
||||
#endif /* HTMLUI_H_ */
|
Loading…
Add table
Reference in a new issue