}
de->de_s = subscription_create_from_channel(de->de_channel, weight,
- buf, st, flags);
+ buf, st, flags,
+ NULL, "DVR",
+ lang_str_get(de->de_title,
+ NULL));
pthread_create(&de->de_thread, NULL, dvr_thread, de);
}
hs->hs_s = subscription_create_from_channel(ch, weight,
htsp->htsp_logname,
- st, 0);
+ st, 0,
+ htsp->htsp_peername,
+ htsp->htsp_username,
+ htsp->htsp_clientname);
return NULL;
}
channels_init();
+ subscription_init();
+
access_init(createdefault);
tcp_server_init();
#include "streaming.h"
#include "channels.h"
#include "service.h"
+#include "htsmsg.h"
+#include "notify.h"
+#include "atomic.h"
struct th_subscription_list subscriptions;
static gtimer_t subscription_reschedule_timer;
}
+/**
+ *
+ */
static void
subscription_reschedule_cb(void *aux)
{
subscription_reschedule();
}
+
/**
*
*/
streaming_msg_free(s->ths_start_message);
free(s->ths_title);
+ free(s->ths_hostname);
+ free(s->ths_username);
+ free(s->ths_client);
free(s);
subscription_reschedule();
+ notify_reload("subscriptions");
}
streaming_msg_free(sm);
return;
}
+
+ if(sm->sm_type == SMT_PACKET) {
+ th_pkt_t *pkt = sm->sm_data;
+ if(pkt->pkt_err)
+ s->ths_total_err++;
+ s->ths_bytes += pkt->pkt_payload->pb_size;
+ }
+
streaming_target_deliver(s->ths_output, sm);
}
*/
static th_subscription_t *
subscription_create(int weight, const char *name, streaming_target_t *st,
- int flags, int direct)
+ int flags, int direct, const char *hostname,
+ const char *username, const char *client)
{
th_subscription_t *s = calloc(1, sizeof(th_subscription_t));
int reject = 0;
+ static int tally;
if(flags & SUBSCRIPTION_RAW_MPEGTS)
reject |= SMT_TO_MASK(SMT_PACKET); // Reject parsed frames
s->ths_weight = weight;
s->ths_title = strdup(name);
+ s->ths_hostname = hostname ? strdup(hostname) : NULL;
+ s->ths_username = username ? strdup(username) : NULL;
+ s->ths_client = client ? strdup(client) : NULL;
s->ths_total_err = 0;
s->ths_output = st;
s->ths_flags = flags;
time(&s->ths_start);
+
+ s->ths_id = ++tally;
+
LIST_INSERT_SORTED(&subscriptions, s, ths_global_link, subscription_sort);
return s;
th_subscription_t *
subscription_create_from_channel(channel_t *ch, unsigned int weight,
const char *name, streaming_target_t *st,
- int flags)
+ int flags, const char *hostname,
+ const char *username, const char *client)
{
- th_subscription_t *s = subscription_create(weight, name, st, flags, 0);
+ th_subscription_t *s = subscription_create(weight, name, st, flags, 0,
+ hostname, username, client);
s->ths_channel = ch;
LIST_INSERT_HEAD(&ch->ch_subscriptions, s, ths_channel_link);
service_source_info_free(&si);
}
+ notify_reload("subscriptions");
return s;
}
subscription_create_from_service(service_t *t, const char *name,
streaming_target_t *st, int flags)
{
- th_subscription_t *s = subscription_create(INT32_MAX, name, st, flags, 1);
+ th_subscription_t *s = subscription_create(INT32_MAX, name, st, flags, 1,
+ NULL, NULL, NULL);
source_info_t si;
int r;
service_source_info_free(&si);
subscription_link_service(s, t);
+ notify_reload("subscriptions");
return s;
}
tvhlog(LOG_NOTICE, "subscription",
"Dummy join %s ok", id);
}
+
+
+
+/**
+ *
+ */
+htsmsg_t *
+subscription_create_msg(th_subscription_t *s)
+{
+ htsmsg_t *m = htsmsg_create_map();
+
+ htsmsg_add_u32(m, "id", s->ths_id);
+ htsmsg_add_u32(m, "start", s->ths_start);
+ htsmsg_add_u32(m, "errors", s->ths_total_err);
+
+ const char *state;
+ switch(s->ths_state) {
+ default:
+ state = "Idle";
+ break;
+
+ case SUBSCRIPTION_TESTING_SERVICE:
+ state = "Testing";
+ break;
+
+ case SUBSCRIPTION_GOT_SERVICE:
+ state = "Running";
+ break;
+
+ case SUBSCRIPTION_BAD_SERVICE:
+ state = "Bad";
+ break;
+ }
+
+
+ htsmsg_add_str(m, "state", state);
+
+ if (s->ths_hostname && s->ths_username && s->ths_client) {
+ htsmsg_add_str(m, "hostname", s->ths_hostname);
+ htsmsg_add_str(m, "username", s->ths_username);
+ htsmsg_add_str(m, "title", s->ths_client);
+ } else {
+ htsmsg_add_str(m, "title", s->ths_title);
+ }
+
+ if(s->ths_channel != NULL)
+ htsmsg_add_str(m, "channel", s->ths_channel->ch_name);
+
+ if(s->ths_service != NULL)
+ htsmsg_add_str(m, "service", s->ths_service->s_nicename);
+
+ return m;
+}
+
+
+static gtimer_t every_sec;
+
+/**
+ *
+ */
+static void
+every_sec_cb(void *aux)
+{
+ th_subscription_t *s;
+ gtimer_arm(&every_sec, every_sec_cb, NULL, 1);
+
+ LIST_FOREACH(s, &subscriptions, ths_global_link) {
+ int errors = s->ths_total_err;
+ int bw = atomic_exchange(&s->ths_bytes, 0);
+
+ htsmsg_t *m = subscription_create_msg(s);
+ htsmsg_delete_field(m, "errors");
+ htsmsg_add_u32(m, "errors", errors);
+ htsmsg_add_u32(m, "bw", bw);
+ htsmsg_add_u32(m, "updateEntry", 1);
+ notify_by_msg("subscriptions", m);
+ }
+}
+
+
+/**
+ *
+ */
+void
+subscription_init(void)
+{
+ gtimer_arm(&every_sec, every_sec_cb, NULL, 1);
+}
#ifndef SUBSCRIPTIONS_H
#define SUBSCRIPTIONS_H
+extern struct th_subscription_list subscriptions;
+
#define SUBSCRIPTION_RAW_MPEGTS 0x1
typedef struct th_subscription {
+
+ int ths_id;
+
LIST_ENTRY(th_subscription) ths_global_link;
int ths_weight;
char *ths_title; /* display title */
time_t ths_start; /* time when subscription started */
int ths_total_err; /* total errors during entire subscription */
+ int ths_bytes; // Reset every second to get aprox. bandwidth
streaming_target_t ths_input;
streaming_message_t *ths_start_message;
+ char *ths_hostname;
+ char *ths_username;
+ char *ths_client;
+
+
} th_subscription_t;
/**
* Prototypes
*/
+void subscription_init(void);
+
void subscription_unsubscribe(th_subscription_t *s);
void subscription_set_weight(th_subscription_t *s, unsigned int weight);
unsigned int weight,
const char *name,
streaming_target_t *st,
- int flags);
+ int flags,
+ const char *hostname,
+ const char *username,
+ const char *client);
th_subscription_t *subscription_create_from_service(struct service *t,
int subscriptions_active(void);
+struct htsmsg;
+struct htsmsg *subscription_create_msg(th_subscription_t *s);
+
#endif /* SUBSCRIPTIONS_H */
#include "epggrab/private.h"
#include "config2.h"
#include "lang_codes.h"
+#include "subscriptions.h"
/**
*
extjs_load(hq, "static/app/dvr.js");
extjs_load(hq, "static/app/epggrab.js");
extjs_load(hq, "static/app/config.js");
+ extjs_load(hq, "static/app/status.js");
/**
* Finally, the app itself
return 0;
}
+
+/**
+ *
+ */
+static int
+extjs_subscriptions(http_connection_t *hc, const char *remain, void *opaque)
+{
+ htsbuf_queue_t *hq = &hc->hc_reply;
+ htsmsg_t *out, *array;
+ th_subscription_t *s;
+
+ pthread_mutex_lock(&global_lock);
+
+ if(http_access_verify(hc, ACCESS_ADMIN)) {
+ pthread_mutex_unlock(&global_lock);
+ return HTTP_STATUS_UNAUTHORIZED;
+ }
+
+ out = htsmsg_create_map();
+ array = htsmsg_create_list();
+
+ LIST_FOREACH(s, &subscriptions, ths_global_link)
+ htsmsg_add_msg(array, NULL, subscription_create_msg(s));
+
+ pthread_mutex_unlock(&global_lock);
+
+ htsmsg_add_msg(out, "entries", array);
+
+ htsmsg_json_serialize(out, hq, 0);
+ htsmsg_destroy(out);
+ http_output_content(hc, "text/x-json; charset=UTF-8");
+ return 0;
+}
+
+
/**
*
*/
http_path_add("/epgobject", NULL, extjs_epgobject, ACCESS_WEB_INTERFACE);
http_path_add("/dvr", NULL, extjs_dvr, ACCESS_WEB_INTERFACE);
http_path_add("/dvrlist", NULL, extjs_dvrlist, ACCESS_WEB_INTERFACE);
+ http_path_add("/subscriptions", NULL, extjs_subscriptions, ACCESS_WEB_INTERFACE);
http_path_add("/ecglist", NULL, extjs_ecglist, ACCESS_WEB_INTERFACE);
http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE);
http_path_add("/languages", NULL, extjs_languages, ACCESS_WEB_INTERFACE);
--- /dev/null
+/**
+ *
+ */
+tvheadend.status = function() {
+
+ tvheadend.subsStore = new Ext.data.JsonStore({
+ root : 'entries',
+ totalProperty : 'totalCount',
+ fields : [ {
+ name : 'id'
+ }, {
+ name : 'hostname'
+ }, {
+ name : 'username'
+ }, {
+ name : 'title'
+ }, {
+ name : 'channel'
+ }, {
+ name : 'service'
+ }, {
+ name : 'state'
+ }, {
+ name : 'errors'
+ }, {
+ name : 'bw'
+ }, {
+ name : 'start',
+ type : 'date',
+ dateFormat : 'U' /* unix time */
+ } ],
+ url : 'subscriptions',
+ autoLoad : true,
+ id : 'id'
+ });
+
+
+
+ tvheadend.comet.on('subscriptions', function(m) {
+
+ if (m.reload != null) tvheadend.subsStore.reload();
+
+ if (m.updateEntry != null) {
+ r = tvheadend.subsStore.getById(m.id)
+ if (typeof r === 'undefined') {
+ tvheadend.subsStore.reload();
+ return;
+ }
+
+ r.data.channel = m.channel;
+ r.data.service = m.service;
+ r.data.state = m.state;
+ r.data.errors = m.errors;
+ r.data.bw = m.bw
+
+ tvheadend.subsStore.afterEdit(r);
+ tvheadend.subsStore.fireEvent('updated', tvheadend.subsStore, r,
+ Ext.data.Record.COMMIT);
+ }
+ });
+
+ function renderDate(value) {
+ var dt = new Date(value);
+ return dt.format('D j M H:i');
+ }
+
+ function renderBw(value) {
+ return parseInt(value / 125);
+ }
+
+ var subsCm = new Ext.grid.ColumnModel([{
+ width : 50,
+ id : 'hostname',
+ header : "Hostname",
+ dataIndex : 'hostname'
+ }, {
+ width : 50,
+ id : 'username',
+ header : "Username",
+ dataIndex : 'username'
+ }, {
+ width : 80,
+ id : 'title',
+ header : "Title",
+ dataIndex : 'title'
+ }, {
+ width : 50,
+ id : 'channel',
+ header : "Channel",
+ dataIndex : 'channel'
+ }, {
+ width : 200,
+ id : 'service',
+ header : "Service",
+ dataIndex : 'service',
+ }, {
+ width : 50,
+ id : 'start',
+ header : "Start",
+ dataIndex : 'start',
+ renderer : renderDate
+ }, {
+ width : 50,
+ id : 'state',
+ header : "State",
+ dataIndex : 'state'
+ }, {
+ width : 50,
+ id : 'errors',
+ header : "Errors",
+ dataIndex : 'errors'
+ }, {
+ width : 50,
+ id : 'bw',
+ header : "Bandwidth (kb/s)",
+ dataIndex : 'bw',
+ renderer: renderBw
+ } ]);
+
+ var panel = new Ext.grid.GridPanel({
+ loadMask : true,
+ stripeRows : true,
+ disableSelection : true,
+ title : 'Active subscriptions',
+ iconCls : 'eye',
+ store : tvheadend.subsStore,
+ cm : subsCm,
+ viewConfig : {
+ forceFit : true
+ }
+ });
+ return panel;
+}
+
tvheadend.rootTabPanel.add(tvheadend.confpanel);
}
+ if (o.admin == true && tvheadend.statuspanel == null) {
+ tvheadend.statuspanel = new tvheadend.status;
+ tvheadend.rootTabPanel.add(tvheadend.statuspanel);
+ }
+
if (tvheadend.aboutPanel == null) {
tvheadend.aboutPanel = new Ext.Panel({
border : false,
}
pthread_mutex_lock(&global_lock);
- s = subscription_create_from_channel(ch, priority, "HTTP", st, flags);
+ s = subscription_create_from_channel(ch, priority, "HTTP", st, flags,
+ NULL, NULL, NULL);
pthread_mutex_unlock(&global_lock);
if(s) {