From ad6ea1b8e1f2e4c381ce5bdfdad0d20bfad51a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Tue, 23 Sep 2008 21:16:59 +0000 Subject: [PATCH] Add tables and web ui editor for auto recording. No backing logic yet though. --- Makefile | 2 +- channels.h | 4 +- dvr/dvr.h | 2 + dvr/dvr_autorec.c | 313 ++++++++++++++++++++++++++++++++ dvr/dvr_db.c | 3 + tvhead.h | 2 +- webui/static/app/dvr.js | 62 ++++++- webui/static/app/tableeditor.js | 1 + 8 files changed, 385 insertions(+), 4 deletions(-) create mode 100644 dvr/dvr_autorec.c diff --git a/Makefile b/Makefile index fcdcb297..be9503c3 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ SRCS = main.c access.c dtable.c tcp.c http.c notify.c epg.c xmltv.c spawn.c SRCS += packet.c streaming.c VPATH += dvr -SRCS += dvr_db.c dvr_rec.c +SRCS += dvr_db.c dvr_rec.c dvr_autorec.c SRCS += channels.c subscriptions.c transports.c diff --git a/channels.h b/channels.h index 9c9ae3e2..1d294c3c 100644 --- a/channels.h +++ b/channels.h @@ -58,7 +58,7 @@ typedef struct channel { struct dvr_entry_list ch_dvrs; - struct autorec_list ch_autorecs; + struct dvr_autorec_entry_list ch_autorecs; struct xmltv_channel *ch_xc; LIST_ENTRY(channel) ch_xc_link; @@ -79,6 +79,8 @@ typedef struct channel_tag { char *ct_comment; char *ct_identifier; struct channel_tag_mapping_list ct_ctms; + + struct dvr_autorec_entry_list ct_autorecs; } channel_tag_t; diff --git a/dvr/dvr.h b/dvr/dvr.h index aa2ce3e7..9dc817e3 100644 --- a/dvr/dvr.h +++ b/dvr/dvr.h @@ -126,6 +126,8 @@ void dvr_entry_create_by_event(event_t *e, const char *creator); void dvr_init(void); +void dvr_autorec_init(void); + void dvr_destroy_by_channel(channel_t *ch); void dvr_rec_subscribe(dvr_entry_t *de); diff --git a/dvr/dvr_autorec.c b/dvr/dvr_autorec.c new file mode 100644 index 00000000..f1cce683 --- /dev/null +++ b/dvr/dvr_autorec.c @@ -0,0 +1,313 @@ +/* + * tvheadend, access control + * 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 +#include +#include +#include + +#include "tvhead.h" +#include "dvr.h" +#include "dtable.h" +#include "epg.h" + +TAILQ_HEAD(dvr_autorec_entry_queue, dvr_autorec_entry); + +struct dvr_autorec_entry_queue autorec_entries; + +typedef struct dvr_autorec_entry { + TAILQ_ENTRY(dvr_autorec_entry) dae_link; + char *dae_id; + + int dae_enabled; + char *dae_creator; + + char *dae_title; + regex_t dae_title_preg; + + epg_content_group_t *dae_ecg; + + channel_t *dae_channel; + LIST_ENTRY(dvr_autorec_entry) dae_channel_link; + + channel_tag_t *dae_channel_tag; + LIST_ENTRY(dvr_autorec_entry) dae_channel_tag_link; + +} dvr_autorec_entry_t; + + + +#if 0 +/** + * return 1 if the event 'e' is matched by the autorec rule 'ar' + */ +static int +autorec_cmp(dvr_autorec_entry_t *dae, event_t *e) +{ + channel_tag_mapping_t *ctm; + + if(dae->dae_channel != NULL && + dae->dae_channel != e->e_channel) + return 0; + + if(dae->dae_channel_tag != NULL) { + LIST_FOREACH(ctm, &dae->dae_channel_tag->ct_ctms, ctm_tag_link) + if(ctm->ctm_channel == e->e_channel) + break; + if(ctm == NULL) + return 0; + } + + + if(dae->dae_ecg != NULL) { + if(e->e_content_type == NULL || + dae->dae_ecg != e->e_content_type->ect_group) + return 0; + } + + if(dae->dae_title != NULL && + regexec(&dae->dae_title_preg, e->e_title, 0, NULL, 0)) + return 0; + + return 1; +} +#endif + + +/** + * + */ +static dvr_autorec_entry_t * +autorec_entry_find(const char *id, int create) +{ + dvr_autorec_entry_t *dae; + char buf[20]; + static int tally; + + if(id != NULL) { + TAILQ_FOREACH(dae, &autorec_entries, dae_link) + if(!strcmp(dae->dae_id, id)) + return dae; + } + + if(create == 0) + return NULL; + + dae = calloc(1, sizeof(dvr_autorec_entry_t)); + if(id == NULL) { + tally++; + snprintf(buf, sizeof(buf), "%d", tally); + id = buf; + } else { + tally = MAX(atoi(id), tally); + } + + dae->dae_id = strdup(id); + TAILQ_INSERT_TAIL(&autorec_entries, dae, dae_link); + return dae; +} + + + +/** + * + */ +static void +autorec_entry_destroy(dvr_autorec_entry_t *dae) +{ + free(dae->dae_id); + + free(dae->dae_creator); + + if(dae->dae_title != NULL) { + free(dae->dae_title); + regfree(&dae->dae_title_preg); + } + + if(dae->dae_channel != NULL) + LIST_REMOVE(dae, dae_channel_link); + + if(dae->dae_channel_tag != NULL) + LIST_REMOVE(dae, dae_channel_tag_link); + + TAILQ_REMOVE(&autorec_entries, dae, dae_link); + free(dae); +} + + +/** + * + */ +static htsmsg_t * +autorec_record_build(dvr_autorec_entry_t *dae) +{ + htsmsg_t *e = htsmsg_create(); + + htsmsg_add_str(e, "id", dae->dae_id); + htsmsg_add_u32(e, "enabled", !!dae->dae_enabled); + htsmsg_add_str(e, "creator", dae->dae_creator ?: ""); + if(dae->dae_channel) + htsmsg_add_str(e, "channel", + dae->dae_channel ? dae->dae_channel->ch_name : ""); + if(dae->dae_channel_tag) + htsmsg_add_str(e, "channeltag", + dae->dae_channel_tag ? dae->dae_channel_tag->ct_name : ""); + htsmsg_add_str(e, "title", dae->dae_title ?: ""); + + return e; +} + +/** + * + */ +static htsmsg_t * +autorec_record_get_all(void *opaque) +{ + htsmsg_t *r = htsmsg_create_array(); + dvr_autorec_entry_t *dae; + + TAILQ_FOREACH(dae, &autorec_entries, dae_link) + htsmsg_add_msg(r, NULL, autorec_record_build(dae)); + + return r; +} + +/** + * + */ +static htsmsg_t * +autorec_record_get(void *opaque, const char *id) +{ + dvr_autorec_entry_t *ae; + + if((ae = autorec_entry_find(id, 0)) == NULL) + return NULL; + return autorec_record_build(ae); +} + + +/** + * + */ +static htsmsg_t * +autorec_record_create(void *opaque) +{ + return autorec_record_build(autorec_entry_find(NULL, 1)); +} + + +/** + * + */ +static htsmsg_t * +autorec_record_update(void *opaque, const char *id, htsmsg_t *values, + int maycreate) +{ + dvr_autorec_entry_t *dae; + const char *s; + channel_t *ch; + channel_tag_t *ct; + + if((dae = autorec_entry_find(id, maycreate)) == NULL) + return NULL; + + tvh_str_set(&dae->dae_creator, htsmsg_get_str(values, "creator")); + + if((s = htsmsg_get_str(values, "channel")) != NULL) { + if(dae->dae_channel != NULL) { + LIST_REMOVE(dae, dae_channel_link); + dae->dae_channel = NULL; + } + if((ch = channel_find_by_name(s, 0)) != NULL) { + LIST_INSERT_HEAD(&ch->ch_autorecs, dae, dae_channel_link); + dae->dae_channel = ch; + } + } + + if((s = htsmsg_get_str(values, "title")) != NULL) { + if(dae->dae_title != NULL) { + free(dae->dae_title); + dae->dae_title = NULL; + regfree(&dae->dae_title_preg); + } + + if(!regcomp(&dae->dae_title_preg, s, + REG_ICASE | REG_EXTENDED | REG_NOSUB)) { + dae->dae_title = strdup(s); + } + } + + if((s = htsmsg_get_str(values, "channeltag")) != NULL) { + if(dae->dae_channel_tag != NULL) { + LIST_REMOVE(dae, dae_channel_tag_link); + dae->dae_channel_tag = NULL; + } + if((ct = channel_tag_find_by_name(s)) != NULL) { + LIST_INSERT_HEAD(&ct->ct_autorecs, dae, dae_channel_tag_link); + dae->dae_channel_tag = ct; + } + } + + return autorec_record_build(dae); +} + + +/** + * + */ +static int +autorec_record_delete(void *opaque, const char *id) +{ + dvr_autorec_entry_t *dae; + + if((dae = autorec_entry_find(id, 0)) == NULL) + return -1; + autorec_entry_destroy(dae); + return 0; +} + + +/** + * + */ +static const dtable_class_t autorec_dtc = { + .dtc_record_get = autorec_record_get, + .dtc_record_get_all = autorec_record_get_all, + .dtc_record_create = autorec_record_create, + .dtc_record_update = autorec_record_update, + .dtc_record_delete = autorec_record_delete, +}; + +/** + * + */ +void +dvr_autorec_init(void) +{ + dtable_t *dt; + + TAILQ_INIT(&autorec_entries); + dt = dtable_create(&autorec_dtc, "autorec", NULL); + dtable_load(dt); +} diff --git a/dvr/dvr_db.c b/dvr/dvr_db.c index e91f916e..e251dc28 100644 --- a/dvr/dvr_db.c +++ b/dvr/dvr_db.c @@ -518,6 +518,9 @@ dvr_init(void) } dvr_db_load(); + + dvr_autorec_init(); + } /** diff --git a/tvhead.h b/tvhead.h index d1a06ed4..9e091cda 100644 --- a/tvhead.h +++ b/tvhead.h @@ -104,7 +104,7 @@ LIST_HEAD(th_muxstream_list, th_muxstream); LIST_HEAD(th_descrambler_list, th_descrambler); TAILQ_HEAD(th_refpkt_queue, th_refpkt); TAILQ_HEAD(th_muxpkt_queue, th_muxpkt); -LIST_HEAD(autorec_list, autorec); +LIST_HEAD(dvr_autorec_entry_list, dvr_autorec_entry); TAILQ_HEAD(th_pktref_queue, th_pktref); LIST_HEAD(streaming_target_list, streaming_target); LIST_HEAD(streaming_component_list, streaming_component); diff --git a/webui/static/app/dvr.js b/webui/static/app/dvr.js index 286fb3c0..7f2ca791 100644 --- a/webui/static/app/dvr.js +++ b/webui/static/app/dvr.js @@ -185,7 +185,66 @@ tvheadend.dvrlog = function() { } return panel; } +/** + * + */ +tvheadend.autoreceditor = function() { + var fm = Ext.form; + + var enabledColumn = new Ext.grid.CheckColumn({ + header: "Enabled", + dataIndex: 'enabled', + width: 60 + }); + var cm = new Ext.grid.ColumnModel([ + enabledColumn, + { + header: "Title (Regexp)", + dataIndex: 'title', + width: 200, + editor: new fm.TextField({allowBlank: true}) + },{ + header: "Channel", + dataIndex: 'channel', + editor: new Ext.form.ComboBox({ + loadingText: 'Loading...', + width: 200, + displayField:'name', + store: tvheadend.channels, + mode: 'local', + editable: false, + triggerAction: 'all', + emptyText: 'Only include channel...' + }) + },{ + header: "Channel tag", + dataIndex: 'channeltag', + editor: new Ext.form.ComboBox({ + displayField:'name', + store: tvheadend.channelTags, + mode: 'local', + editable: false, + triggerAction: 'all', + emptyText: 'Only include tag...' + }) + },{ + header: "Creator", + dataIndex: 'creator', + editor: new fm.TextField({allowBlank: false}) + } + ]); + + var rec = Ext.data.Record.create([ + 'enabled','title','channel','channeltag','creator' + ]); + + return new tvheadend.tableEditor('Automatic Recorder', + 'autorec', cm, rec, + [enabledColumn]); + + +} /** * */ @@ -195,7 +254,8 @@ tvheadend.dvr = function() { activeTab:0, autoScroll:true, title: 'Digital Video Recorder', - items: [new tvheadend.dvrlog + items: [new tvheadend.dvrlog, + new tvheadend.autoreceditor ] }); diff --git a/webui/static/app/tableeditor.js b/webui/static/app/tableeditor.js index bcfa061c..aeceec59 100644 --- a/webui/static/app/tableeditor.js +++ b/webui/static/app/tableeditor.js @@ -8,6 +8,7 @@ tvheadend.tableEditor = function(title, dtable, cm, rec, plugins) { fields: rec, url: "tablemgr", autoLoad: true, + id: 'id', baseParams: {table: dtable, op: "get"} });