const idclass_t access_entry_class = {
.ic_class = "access",
.ic_caption = "Access",
+ .ic_event = "access",
.ic_save = access_entry_class_save,
.ic_get_title = access_entry_class_get_title,
.ic_delete = access_entry_class_delete,
const idclass_t channel_class = {
.ic_class = "channel",
.ic_caption = "Channel",
+ .ic_event = "channel",
.ic_save = channel_class_save,
.ic_get_title = channel_class_get_title,
.ic_delete = channel_class_delete,
const idclass_t channel_tag_class = {
.ic_class = "channeltag",
.ic_caption = "Channel Tag",
+ .ic_event = "channeltag",
.ic_save = channel_tag_class_save,
.ic_get_title = channel_tag_class_get_title,
.ic_delete = channel_tag_class_delete,
int dvr_entry_get_mc( dvr_entry_t *de);
-void dvr_entry_notify(dvr_entry_t *de);
-
void dvr_entry_save(dvr_entry_t *de);
const char *dvr_entry_status(dvr_entry_t *de);
#include "tvheadend.h"
#include "settings.h"
#include "dvr.h"
-#include "notify.h"
#include "dtable.h"
#include "epg.h"
const idclass_t dvr_autorec_entry_class = {
.ic_class = "dvrautorec",
.ic_caption = "DVR Auto-Record Entry",
+ .ic_event = "dvrautorec",
.ic_save = dvr_autorec_entry_class_save,
.ic_get_title = dvr_autorec_entry_class_get_title,
.ic_delete = dvr_autorec_entry_class_delete,
autorec_destroy_by_channel(channel_t *ch, int delconf)
{
dvr_autorec_entry_t *dae;
- htsmsg_t *m;
while((dae = LIST_FIRST(&ch->ch_autorecs)) != NULL)
autorec_entry_destroy(dae, delconf);
-
- /* Notify web clients that we have messed with the tables */
- m = htsmsg_create_map();
- htsmsg_add_u32(m, "reload", 1);
- notify_by_msg("autorec", m);
}
/*
autorec_destroy_by_channel_tag(channel_tag_t *ct, int delconf)
{
dvr_autorec_entry_t *dae;
- htsmsg_t *m;
while((dae = LIST_FIRST(&ct->ct_autorecs)) != NULL) {
LIST_REMOVE(dae, dae_channel_tag_link);
dae->dae_channel_tag = NULL;
+ idnode_notify_simple(&dae->dae_id);
if (delconf)
dvr_autorec_save(dae);
}
-
- /* Notify web clients that we have messed with the tables */
- m = htsmsg_create_map();
- htsmsg_add_u32(m, "reload", 1);
- notify_by_msg("autorec", m);
}
#include "tvheadend.h"
#include "dvr.h"
-#include "notify.h"
#include "htsp_server.h"
#include "streaming.h"
#include "intlconv.h"
}
}
-/**
- *
- */
-void
-dvr_entry_notify(dvr_entry_t *de)
-{
- htsmsg_t *m = htsmsg_create_map();
-
- htsmsg_add_u32(m, "updateEntry", 1);
- htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&de->de_id));
- htsmsg_add_str(m, "status", dvr_entry_status(de));
- htsmsg_add_str(m, "schedstate", dvr_entry_schedstatus(de));
- notify_by_msg("dvrdb", m);
-}
-
-
/**
*
*/
/* Save changes */
if (save) {
- dvr_entry_save(de);
+ idnode_changed(&de->de_id);
htsp_dvr_entry_update(de);
- dvr_entry_notify(de);
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\": Updated Timer",
lang_str_get(de->de_title, NULL), DVR_CH_NAME(de));
}
*
*/
static void
-dvr_stop_recording(dvr_entry_t *de, int stopcode, int delconf)
+dvr_stop_recording(dvr_entry_t *de, int stopcode, int saveconf)
{
dvr_config_t *cfg = de->de_config;
lang_str_get(de->de_title, NULL), DVR_CH_NAME(de),
dvr_entry_status(de));
- if (delconf)
+ if (saveconf)
+ idnode_changed(&de->de_id);
+ else
+ idnode_notify_simple(&de->de_id);
dvr_entry_save(de);
htsp_dvr_entry_update(de);
- dvr_entry_notify(de);
gtimer_arm_abs(&de->de_timer, dvr_timer_expire, de,
de->de_stop + cfg->dvr_retention_days * 86400);
tvhlog(LOG_INFO, "dvr", "\"%s\" on \"%s\" recorder starting",
lang_str_get(de->de_title, NULL), DVR_CH_NAME(de));
- dvr_entry_notify(de);
+ idnode_changed(&de->de_id);
htsp_dvr_entry_update(de);
dvr_rec_subscribe(de);
const idclass_t dvr_entry_class = {
.ic_class = "dvrentry",
.ic_caption = "DVR Entry",
+ .ic_event = "dvrentry",
.ic_save = dvr_entry_class_save,
.ic_get_title = dvr_entry_class_get_title,
.ic_delete = dvr_entry_class_delete,
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "intlconv/charsets");
- htsmsg_add_str(m, "event", "charsets");
return m;
}
const idclass_t dvr_config_class = {
.ic_class = "dvrconfig",
.ic_caption = "DVR Configuration Profile",
+ .ic_event = "dvrconfig",
.ic_save = dvr_config_class_save,
.ic_get_title = dvr_config_class_get_title,
.ic_delete = dvr_config_class_delete,
dvr_inotify_del(de);
htsp_dvr_entry_update(de);
- dvr_entry_notify(de);
+ idnode_notify_simple(&de->de_id);
}
/*
while ((de = LIST_FIRST(&die->entries))) {
htsp_dvr_entry_update(de);
- dvr_entry_notify(de);
+ idnode_notify_simple(&de->de_id);
dvr_inotify_del(de);
}
}
de->de_errors++;
}
if (notify)
- dvr_entry_notify(de);
+ idnode_notify_simple(&de->de_id);
}
/**
dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0);
if(dvr_rec_start(de, sm->sm_data) == 0) {
started = 1;
- dvr_entry_notify(de);
+ idnode_changed(&de->de_id);
htsp_dvr_entry_update(de);
- dvr_entry_save(de);
}
pthread_mutex_unlock(&global_lock);
}
const idclass_t esfilter_class = {
.ic_class = "esfilter",
.ic_caption = "Elementary Stream Filter",
+ .ic_event = "esfilter",
.ic_save = esfilter_class_save,
.ic_get_title = esfilter_class_get_title,
.ic_delete = esfilter_class_delete,
f->hmf_str = strdup(str);
}
+/*
+ *
+ */
+int
+htsmsg_set_str(htsmsg_t *msg, const char *name, const char *str)
+{
+ htsmsg_field_t *f = htsmsg_field_find(msg, name);
+ if (!f)
+ f = htsmsg_field_add(msg, name, HMF_STR, HMF_ALLOCED | HMF_NAME_ALLOCED);
+ else {
+ if (f->hmf_type != HMF_STR)
+ return 1;
+ if(f->hmf_flags & HMF_ALLOCED)
+ free((void *)f->hmf_str);
+ }
+ f->hmf_str = strdup(str);
+ return 0;
+}
+
/*
*
*/
*/
void htsmsg_add_str(htsmsg_t *msg, const char *name, const char *str);
+/**
+ * Add/update a string field
+ */
+int htsmsg_set_str(htsmsg_t *msg, const char *name, const char *str);
+
/**
* Add an field where source is a list or map message.
*/
idclass_register(class); // Note: we never actually unregister
/* Fire event */
- idnode_notify(in, NULL, 0, 1);
+ idnode_notify_simple(in);
return 0;
}
lock_assert(&global_lock);
RB_REMOVE(&idnodes, in, in_link);
tvhtrace("idnode", "unlink node %s", idnode_uuid_as_str(in));
- idnode_notify(in, NULL, 0, 1);
+ idnode_notify_simple(in);
}
/**
if (save && dosave)
idnode_savefn(self);
if (dosave)
- idnode_notify(self, NULL, 0, 0);
+ idnode_notify_simple(self);
// Note: always output event if "dosave", reason is that UI updates on
// these, but there are some subtle cases where it will expect
// an update and not get one. This include fields being set for
return save;
}
+void
+idnode_changed( idnode_t *self )
+{
+ idnode_notify_simple(self);
+ idnode_savefn(self);
+}
+
/* **************************************************************************
* Read
* *************************************************************************/
return NULL;
}
+static const char *
+idclass_get_event (const idclass_t *idc)
+{
+ while (idc) {
+ if (idc->ic_event)
+ return idc->ic_event;
+ idc = idc->ic_super;
+ }
+ return NULL;
+}
+
static const char *
idclass_get_order (const idclass_t *idc)
{
htsmsg_add_str(m, "caption", s);
if ((s = idclass_get_class(idc)))
htsmsg_add_str(m, "class", s);
+ if ((s = idclass_get_event(idc)))
+ htsmsg_add_str(m, "event", s);
if ((s = idclass_get_order(idc)))
htsmsg_add_str(m, "order", s);
if ((p = idclass_get_property_groups(idc)))
htsmsg_add_str(m, "caption", s);
if ((s = idclass_get_class(idc)))
htsmsg_add_str(m, "class", s);
+ if ((s = idclass_get_event(idc)))
+ htsmsg_add_str(m, "event", s);
htsmsg_add_msg(m, "params", idnode_params(idc, self, list, optmask));
}
/* **************************************************************************
- * Notifcation
+ * Notification
* *************************************************************************/
+/**
+ * Delayed notification
+ */
+static void
+idnode_notify_delayed ( idnode_t *in, const char *uuid, const char *event )
+{
+ pthread_mutex_lock(&idnode_mutex);
+ if (!idnode_queue)
+ idnode_queue = htsmsg_create_map();
+ htsmsg_set_str(idnode_queue, uuid, event);
+ pthread_cond_signal(&idnode_cond);
+ pthread_mutex_unlock(&idnode_mutex);
+}
+
/**
* Update internal event pipes
*/
const idclass_t *ic = in->in_class;
const char *uuid = idnode_uuid_as_str(in);
while (ic) {
- if (ic->ic_event) {
- htsmsg_t *m = htsmsg_create_map();
- htsmsg_add_str(m, "uuid", uuid);
- notify_by_msg(ic->ic_event, m);
- }
+ if (ic->ic_event)
+ idnode_notify_delayed(in, uuid, ic->ic_event);
ic = ic->ic_super;
}
}
*/
void
idnode_notify
- (idnode_t *in, const char *chn, int force, int event)
+ (idnode_t *in, int event)
{
const char *uuid = idnode_uuid_as_str(in);
if (!tvheadend_running)
return;
- /* Forced */
- if (chn || force) {
- htsmsg_t *m = htsmsg_create_map();
- htsmsg_add_str(m, "uuid", uuid);
- notify_by_msg(chn ?: "idnodeUpdated", m);
+ /* Immediate */
+ if (!event) {
+
+ const idclass_t *ic = in->in_class;
+
+ while (ic) {
+ if (ic->ic_event) {
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_str(m, "uuid", uuid);
+ notify_by_msg(ic->ic_event, m);
+ }
+ ic = ic->ic_super;
+ }
/* Rate-limited */
} else {
- pthread_mutex_lock(&idnode_mutex);
- if (!idnode_queue)
- idnode_queue = htsmsg_create_map();
- htsmsg_set_u32(idnode_queue, uuid, 1);
- pthread_cond_signal(&idnode_cond);
- pthread_mutex_unlock(&idnode_mutex);
- }
-
- /* Send event */
- if (event)
idnode_notify_event(in);
+ }
}
void
idnode_notify_simple (void *in)
{
- idnode_notify(in, NULL, 0, 0);
+ idnode_notify(in, 1);
}
void
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", idnode_uuid_as_str(in));
htsmsg_add_str(m, "text", idnode_get_title(in));
- notify_by_msg("idnodeUpdated", m);
+ notify_by_msg("title", m);
idnode_notify_event(in);
}
idnode_t *node;
htsmsg_t *m, *q = NULL;
htsmsg_field_t *f;
+ const char *event;
pthread_mutex_lock(&idnode_mutex);
pthread_mutex_lock(&global_lock);
HTSMSG_FOREACH(f, q) {
- node = idnode_find(f->hmf_name, NULL);
- m = htsmsg_create_map();
+ node = idnode_find(f->hmf_name, NULL);
+ event = htsmsg_field_get_str(f);
+ m = htsmsg_create_map();
htsmsg_add_str(m, "uuid", f->hmf_name);
- if (node)
- notify_by_msg("idnodeUpdated", m);
- else
- notify_by_msg("idnodeDeleted", m);
+ if (!node)
+ htsmsg_add_u32(m, "removed", 1);
+ notify_by_msg(event, m);
}
/* Finished */
void idnode_moveup (idnode_t *in);
void idnode_movedown (idnode_t *in);
+void idnode_changed (idnode_t *in);
+
void *idnode_find (const char *uuid, const idclass_t *idc);
idnode_set_t *idnode_find_all(const idclass_t *idc);
-#define idnode_updated(in) idnode_notify(in, NULL, 0, 0)
-void idnode_notify
- (idnode_t *in, const char *chn, int force, int event);
+
+void idnode_notify (idnode_t *in, int event);
void idnode_notify_simple (void *in);
void idnode_notify_title_changed (void *in);
/* Load config */
if (conf)
idnode_load(&th->th_id, conf);
-
- /* Update */
- notify_reload("hardware");
return o;
}
// TODO
LIST_REMOVE(th, th_link);
idnode_unlink(&th->th_id);
- notify_reload("hardware");
}
/*
/* Save details */
if (save) {
- idnode_updated(&s->s_id);
- s->s_config_save((service_t*)s);
+ idnode_changed(&s->s_id);
service_refresh_channel((service_t*)s);
}
}
{
.ic_class = "linuxdvb_adapter",
.ic_caption = "LinuxDVB Adapter",
+ .ic_event = "linuxdvb_adapter",
.ic_save = linuxdvb_adapter_class_save,
.ic_get_childs = linuxdvb_adapter_class_get_childs,
.ic_get_title = linuxdvb_adapter_class_get_title,
{
.ic_class = "linuxdvb_satconf",
.ic_caption = "DVB-S Satconf",
+ .ic_event = "linuxdvb_satconf",
.ic_get_title = linuxdvb_satconf_class_get_title,
.ic_save = linuxdvb_satconf_class_save,
.ic_properties = (const property_t[]) {
{
.ic_class = "linuxdvb_satconf_ele",
.ic_caption = "Satconf",
+ .ic_event = "linuxdvb_satconf_ele",
.ic_get_title = linuxdvb_satconf_ele_class_get_title,
.ic_get_childs = linuxdvb_satconf_ele_class_get_childs,
.ic_save = linuxdvb_satconf_ele_class_save,
{
.ic_class = "linuxdvb_diseqc",
.ic_caption = "DiseqC",
+ .ic_event = "linuxdvb_diseqc",
.ic_get_title = linuxdvb_diseqc_class_get_title,
.ic_save = linuxdvb_diseqc_class_save,
};
{
.ic_class = "mpegts_input",
.ic_caption = "MPEGTS Input",
+ .ic_event = "mpegts_input",
.ic_get_title = mpegts_input_class_get_title,
.ic_properties = (const property_t[]){
{
static void
mpegts_network_scan_notify ( mpegts_mux_t *mm )
{
- idnode_updated(&mm->mm_id);
- idnode_updated(&mm->mm_network->mn_id);
+ idnode_notify_simple(&mm->mm_id);
+ idnode_notify_simple(&mm->mm_network->mn_id);
}
static int
{
int r;
char buf[256];
- service_create0((service_t*)s, class, uuid, S_MPEG_TS, conf);
+
+ if (service_create0((service_t*)s, class, uuid, S_MPEG_TS, conf) == NULL)
+ return NULL;
/* Create */
sbuf_init(&s->s_tsbuf);
tvhlog(LOG_DEBUG, "mpegts", "%s - add service %04X %s", buf, s->s_dvb_service_id, s->s_dvb_svcname);
/* Notification */
- idnode_updated(&mm->mm_id);
- idnode_updated(&mm->mm_network->mn_id);
+ idnode_notify_simple(&mm->mm_id);
+ idnode_notify_simple(&mm->mm_network->mn_id);
return s;
}
{
.ic_class = "satip_satconf",
.ic_caption = "Satconf",
+ .ic_event = "satip_satconf",
.ic_get_title = satip_satconf_class_get_title,
.ic_save = satip_satconf_class_save,
.ic_properties = (const property_t[]) {
const idclass_t service_class = {
.ic_class = "service",
.ic_caption = "Service",
+ .ic_event = "service",
.ic_save = service_class_save,
.ic_get_title = service_class_get_title,
.ic_properties = (const property_t[]){
tvheadend.idnode_grid(panel, {
url: 'api/access/entry',
- comet: 'acl_entries',
titleS: 'Access Entry',
titleP: 'Access Entries',
columns: {
scope.insert(0,new placeholder({key: '-1', val: '(Clear filter)'}));
};
-tvheadend.channelTags = new Ext.data.JsonStore({
+tvheadend.channelTags = tvheadend.idnode_get_enum({
url: 'api/channeltag/list',
- root: 'entries',
- fields: ['key', 'val'],
- id: 'key',
- autoLoad: true,
- sortInfo: {
- field: 'val',
- direction: 'ASC',
- },
+ event: 'channeltag',
listeners: {
'load': insertChannelTagsClearOption
}
for (x = 0; x < response.messages.length; x++) {
m = response.messages[x];
try {
+ console.log('comet:' + m.notificationClass);
tvheadend.comet.fireEvent(m.notificationClass, m);
} catch (e) {
tvheadend.log('comet failure [e=' + e.message + ']');
tvheadend.idnode_grid(panel, {
url: 'api/dvr/entry',
gridURL: 'api/dvr/entry/grid_upcoming',
- comet: 'dvrentry',
titleS: 'Upcoming Recording',
titleP: 'Upcoming Recordings',
iconCls: 'clock',
url: 'api/dvr/entry',
gridURL: 'api/dvr/entry/grid_finished',
readonly: true,
- comet: 'dvrentry',
titleS: 'Finished Recording',
titleP: 'Finished Recordings',
iconCls: 'television',
tvheadend.idnode_grid(panel, {
url: 'api/dvr/entry',
gridURL: 'api/dvr/entry/grid_failed',
- readonly: true,
comet: 'dvrentry',
+ readonly: true,
titleS: 'Failed Recording',
titleP: 'Failed Recordings',
iconCls: 'exclamation',
tvheadend.idnode_grid(panel, {
url: 'api/dvr/autorec',
- comet: 'dvrautorec',
titleS: 'DVR AutoRec Entry',
titleP: 'DVR AutoRec Entries',
iconCls: 'wand',
{
tvheadend.idnode_grid(panel, {
url: 'api/esfilter/video',
- comet: 'esfilter_video',
titleS: 'Video Stream Filter',
titleP: 'Video Stream Filters',
tabIndex: 0,
tvheadend.idnode_grid(panel, {
url: 'api/esfilter/audio',
- comet: 'esfilter_audio',
titleS: 'Audio Stream Filter',
titleP: 'Audio Stream Filters',
tabIndex: 1,
tvheadend.idnode_grid(panel, {
url: 'api/esfilter/teletext',
- comet: 'esfilter_teletext',
titleS: 'Teletext Stream Filter',
titleP: 'Teletext Stream Filters',
tabIndex: 2,
tvheadend.idnode_grid(panel, {
url: 'api/esfilter/subtit',
- comet: 'esfilter_subtit',
titleS: 'Subtitle Stream Filter',
titleP: 'Subtitle Stream Filters',
tabIndex: 3,
tvheadend.idnode_grid(panel, {
url: 'api/esfilter/ca',
- comet: 'esfilter_ca',
titleS: 'CA Stream Filter',
titleP: 'CA Stream Filters',
tabIndex: 4,
tvheadend.idnode_grid(panel, {
url: 'api/esfilter/other',
- comet: 'esfilter_other',
titleS: 'Other Stream Filter',
titleP: 'Other Stream Filters',
tabIndex: 5,
*/
this.clazz = conf.class;
this.text = conf.caption || this.clazz;
+ this.event = conf.event;
this.props = conf.props;
this.order = [];
this.groups = conf.groups;
};
if (conf.comet)
tvheadend.comet.on(conf.comet, update);
- tvheadend.comet.on('idnodeUpdated', update);
- tvheadend.comet.on('idnodeDeleted', update);
+ if (idnode.event && idnode.event != conf.comet)
+ tvheadend.comet.on(idnode.event, update);
}
/* Request data */
};
if (conf.comet)
tvheadend.comet.on(conf.comet, update);
- tvheadend.comet.on('idnodeUpdated', update);
- tvheadend.comet.on('idnodeDeleted', update);
};
/*
tvheadend.idnode_tree = function(conf)
{
var current = null;
+ var events = {};
var params = conf.params || {};
var loader = new Ext.tree.TreeLoader({
dataUrl: conf.url,
nodeParameter: 'uuid'
});
+ loader.on('load', function(l, n, r) {
+ if (n.uuid && n.event && !(n.event in events)) {
+ events[n.event] = 1;
+ tvheadend.comet.on(n.event, function(o) {
+ if (o.uuid)
+ tree.getRootNode().reload();
+ });
+ }
+ });
+
var tree = new Ext.tree.TreePanel({
loader: loader,
flex: 1,
});
}
- // TODO: top-level reload
- tvheadend.comet.on('idnodeUpdated', function(o) {
+ tvheadend.comet.on('title', function(o) {
var n = tree.getNodeById(o.uuid);
if (n) {
if (o.text)
{
tvheadend.idnode_grid(panel, {
url: 'api/mpegts/network',
- comet: 'mpegts_network',
titleS: 'Network',
titleP: 'Networks',
tabIndex: 1,
{
tvheadend.idnode_grid(panel, {
url: 'api/mpegts/mux',
- comet: 'mpegts_mux',
titleS: 'Mux',
titleP: 'Muxes',
tabIndex: 2,
});
tvheadend.idnode_grid(panel, {
url: 'api/mpegts/service',
- comet: 'service',
titleS: 'Service',
titleP: 'Services',
tabIndex: 3,
{
tvheadend.idnode_grid(panel, {
url: 'api/mpegts/mux_sched',
- comet: 'mpegts_mux_sched',
titleS: 'Mux Scheduler',
titleP: 'Mux Schedulers',
tabIndex: 4,
return tvheadend.idnode_tree({
url: 'api/hardware/tree',
title: 'TV adapters',
- comet: 'hardware',
help: function() {
new tvheadend.help('TV adapters', 'config_tvadapters.html');
}