/* * tvheadend, WEBUI / HTML user interface * Copyright (C) 2008 Andreas Öman * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "tvhead.h" #include "http.h" #include "webui.h" #include "access.h" #include "epg.h" #include "pvr.h" #define ACCESS_SIMPLE \ (ACCESS_WEB_INTERFACE | ACCESS_RECORDER_VIEW | ACCESS_RECORDER_CHANGE) static struct strtab recstatustxt[] = { { "Recording scheduled", HTSTV_PVR_STATUS_SCHEDULED }, { "Recording", HTSTV_PVR_STATUS_RECORDING }, { "Recording completed", 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 }, }; const char *days[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; 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; } /** * Compare start for two events, used as argument to qsort(3) */ static int eventcmp(const void *A, const void *B) { const event_t *a = *(const event_t **)A; const event_t *b = *(const event_t **)B; return a->e_start - b->e_start; } 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; } /** * Root page, we direct the client to different pages depending * on if it is a full blown browser or just some mobile app */ static int page_root(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { if(is_client_simple(hc)) { http_redirect(hc, hr, "/simple.html"); } else { http_redirect(hc, hr, "/ajax/index.html"); } return 0; } /** * Root page, we direct the client to different pages depending * on if it is a full blown browser or just some mobile app */ static int page_simple(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { tcp_queue_t *tq = &hr->hr_tq; const char *s = http_arg_get(&hc->hc_req_args, "s"); struct event_list events; event_t *e, **ev; int c, k, i; struct tm a, b, day; time_t stop; pvr_rec_t *pvrr, **pv; const char *rstatus; tcp_qprintf(tq, ""); tcp_qprintf(tq, ""); tcp_qprintf(tq, "
"); tcp_qprintf(tq, "Event: "); tcp_qprintf(tq, ""); tcp_qprintf(tq, "

"); if(s != NULL) { LIST_INIT(&events); c = epg_search(&events, s, NULL, NULL, NULL); if(c == -1) { tcp_qprintf(tq, "Event title: Regular expression syntax error"); } else if(c == 0) { tcp_qprintf(tq, "No matching entries found"); } else { tcp_qprintf(tq, "%d entries found", c); ev = alloca(c * sizeof(event_t *)); c = 0; LIST_FOREACH(e, &events, e_tmp_link) ev[c++] = e; qsort(ev, c, sizeof(event_t *), eventcmp); if(c > 25) { c = 25; tcp_qprintf(tq, ", %d entries shown", c); } tcp_qprintf(tq, ""); memset(&day, -1, sizeof(struct tm)); for(k = 0; k < c; k++) { e = ev[k]; localtime_r(&e->e_start, &a); stop = e->e_start + e->e_duration; localtime_r(&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, "
%s, %d/%d
", days[day.tm_wday], day.tm_mday, day.tm_mon + 1); } pvrr = pvr_get_by_entry(e); rstatus = pvrr != NULL ? val2str(pvrr->pvrr_status, recstatustxt) : NULL; tcp_qprintf(tq, "" "%02d:%02d-%02d:%02d %s%s%s
", e->e_tag, a.tm_hour, a.tm_min, b.tm_hour, b.tm_min, e->e_title, rstatus ? " " : "", rstatus ?: ""); } } tcp_qprintf(tq, "
"); } tcp_qprintf(tq, "Recorder log:
\n"); c = 0; LIST_FOREACH(pvrr, &pvrr_global_list, pvrr_global_link) c++; if(c == 0) { tcp_qprintf(tq, "No entries
"); } 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]; 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, "
%s, %d/%d
", days[day.tm_wday], day.tm_mday, day.tm_mon + 1); } rstatus = val2str(pvrr->pvrr_status, recstatustxt); tcp_qprintf(tq, "", pvrr->pvrr_ref); tcp_qprintf(tq, "%02d:%02d-%02d:%02d  %s", a.tm_hour, a.tm_min, b.tm_hour, b.tm_min, pvrr->pvrr_title); tcp_qprintf(tq, ""); tcp_qprintf(tq, "
(%s)

", rstatus); } tcp_qprintf(tq, ""); http_output_html(hc, hr); return 0; } /** * Event info, deliver info about the given event */ static int page_einfo(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { tcp_queue_t *tq = &hr->hr_tq; event_t *e; struct tm a, b; time_t stop; pvr_rec_t *pvrr = NULL; tv_pvr_status_t pvrstatus; const char *rstatus; if(remain == NULL || (e = epg_event_find_by_tag(atoi(remain))) == NULL) return 404; pvrr = pvr_get_by_entry(e); if((http_arg_get(&hc->hc_req_args, "rec")) != NULL) { pvrr = pvr_schedule_by_event(e, hc->hc_username); } else if(pvrr && (http_arg_get(&hc->hc_req_args, "clear")) != NULL) { pvr_clear(pvrr); pvrr = NULL; } else if(pvrr && (http_arg_get(&hc->hc_req_args, "cancel")) != NULL) { pvr_abort(pvrr); } tcp_qprintf(tq, ""); tcp_qprintf(tq, ""); localtime_r(&e->e_start, &a); stop = e->e_start + e->e_duration; localtime_r(&stop, &b); tcp_qprintf(tq, "%s, %d/%d %02d:%02d - %02d:%02d
", days[a.tm_wday], a.tm_mday, a.tm_mon + 1, a.tm_hour, a.tm_min, b.tm_hour, b.tm_min); tcp_qprintf(tq, "
\"%s\": \"%s\"

", e->e_channel->ch_name, e->e_title); pvrstatus = pvrr != NULL ? pvrr->pvrr_status : HTSTV_PVR_STATUS_NONE; if((rstatus = val2str(pvrstatus, recstatustxt)) != NULL) tcp_qprintf(tq, "Recording status: %s
", rstatus); tcp_qprintf(tq, "
", e->e_tag); switch(pvrstatus) { case HTSTV_PVR_STATUS_SCHEDULED: tcp_qprintf(tq, ""); break; case HTSTV_PVR_STATUS_RECORDING: tcp_qprintf(tq, ""); break; case HTSTV_PVR_STATUS_NONE: tcp_qprintf(tq, ""); break; default: tcp_qprintf(tq, ""); break; } tcp_qprintf(tq, "
"); tcp_qprintf(tq, "%s", e->e_desc); tcp_qprintf(tq, "
New search
"); tcp_qprintf(tq, ""); http_output_html(hc, hr); return 0; } /** * PVR info, deliver info about the given PVR entry */ static int page_pvrinfo(http_connection_t *hc, http_reply_t *hr, const char *remain, void *opaque) { tcp_queue_t *tq = &hr->hr_tq; struct tm a, b; time_t stop; pvr_rec_t *pvrr = NULL; tv_pvr_status_t pvrstatus; const char *rstatus; if(remain == NULL || (pvrr = pvr_get_tag_entry(atoi(remain))) == NULL) return 404; if((http_arg_get(&hc->hc_req_args, "clear")) != NULL) { pvr_clear(pvrr); pvrr = NULL; http_redirect(hc, hr, "/simple.html"); return 0; } else if((http_arg_get(&hc->hc_req_args, "cancel")) != NULL) { pvr_abort(pvrr); } tcp_qprintf(tq, ""); tcp_qprintf(tq, ""); localtime_r(&pvrr->pvrr_start, &a); stop = pvrr->pvrr_start + pvrr->pvrr_stop; localtime_r(&stop, &b); tcp_qprintf(tq, "%s, %d/%d %02d:%02d - %02d:%02d
", days[a.tm_wday], a.tm_mday, a.tm_mon + 1, a.tm_hour, a.tm_min, b.tm_hour, b.tm_min); tcp_qprintf(tq, "
\"%s\": \"%s\"

", pvrr->pvrr_channel->ch_name, pvrr->pvrr_title); pvrstatus = pvrr->pvrr_status; if((rstatus = val2str(pvrstatus, recstatustxt)) != NULL) tcp_qprintf(tq, "Recording status: %s
", rstatus); tcp_qprintf(tq, "
", pvrr->pvrr_ref); switch(pvrstatus) { case HTSTV_PVR_STATUS_SCHEDULED: tcp_qprintf(tq, ""); break; case HTSTV_PVR_STATUS_RECORDING: tcp_qprintf(tq, ""); break; case HTSTV_PVR_STATUS_DONE: tcp_qprintf(tq, ""); break; default: tcp_qprintf(tq, ""); break; } tcp_qprintf(tq, "
"); tcp_qprintf(tq, "%s", pvrr->pvrr_desc); tcp_qprintf(tq, "
New search
"); tcp_qprintf(tq, ""); http_output_html(hc, hr); return 0; } /** * WEB user interface */ void webui_start(void) { http_path_add("/", NULL, page_root, ACCESS_WEB_INTERFACE); http_path_add("/simple.html", NULL, page_simple, ACCESS_SIMPLE); http_path_add("/eventinfo", NULL, page_einfo, ACCESS_SIMPLE); http_path_add("/pvrinfo", NULL, page_pvrinfo, ACCESS_SIMPLE); }