#include "redblack.h"
#include "access.h"
-#define TVH_API_VERSION 14
+#define TVH_API_VERSION 15
/*
* Command hook
#include "access.h"
#include "api.h"
#include "epg.h"
+#include "imagecache.h"
#include "dvr/dvr.h"
+static htsmsg_t *
+api_epg_get_list ( const char *s )
+{
+ htsmsg_t *m = NULL;
+ char *r, *saveptr;
+ if (s && s[0] != '\0') {
+ s = r = strdup(s);
+ r = strtok_r(r, ";", &saveptr);
+ while (r) {
+ while (*r != '\0' && *r <= ' ')
+ r++;
+ if (*r != '\0') {
+ if (m == NULL)
+ m = htsmsg_create_list();
+ htsmsg_add_str(m, NULL, r);
+ }
+ r = strtok_r(NULL, ";", &saveptr);
+ }
+ free((char *)s);
+ }
+ return m;
+}
+
+static void
+api_epg_add_channel ( htsmsg_t *m, channel_t *ch )
+{
+ int64_t chnum;
+ char buf[32];
+ htsmsg_add_str(m, "channelName", channel_get_name(ch));
+ htsmsg_add_str(m, "channelUuid", channel_get_uuid(ch));
+ if ((chnum = channel_get_number(ch)) >= 0) {
+ uint32_t maj = chnum / CHANNEL_SPLIT;
+ uint32_t min = chnum % CHANNEL_SPLIT;
+ if (min)
+ snprintf(buf, sizeof(buf), "%u.%u", maj, min);
+ else
+ snprintf(buf, sizeof(buf), "%u", maj);
+ htsmsg_add_str(m, "channelNumber", buf);
+ }
+ if (ch->ch_icon)
+ htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
+}
+
static htsmsg_t *
api_epg_entry ( epg_broadcast_t *eb, const char *lang )
{
char buf[64];
epg_episode_t *ee = eb->episode;
channel_t *ch = eb->channel;
- htsmsg_t *m;
+ htsmsg_t *m, *m2;
epg_episode_num_t epnum;
+ epg_genre_t *eg;
dvr_entry_t *de;
if (!ee || !ch) return NULL;
m = htsmsg_create_map();
/* EPG IDs */
- // Note: "id" is for UI compat, remove it?
- htsmsg_add_u32(m, "id", eb->id);
htsmsg_add_u32(m, "eventId", eb->id);
if (ee) {
htsmsg_add_u32(m, "episodeId", ee->id);
}
/* Channel Info */
- // Note: "channel" is for UI compat, remove it?
- htsmsg_add_str(m, "channel", channel_get_name(ch));
- htsmsg_add_str(m, "channelName", channel_get_name(ch));
- htsmsg_add_str(m, "channelUuid", channel_get_uuid(ch));
- htsmsg_add_u32(m, "channelId", channel_get_id(ch));
+ api_epg_add_channel(m, ch);
/* Time */
htsmsg_add_s64(m, "start", eb->start);
htsmsg_add_s64(m, "stop", eb->stop);
- htsmsg_add_s64(m, "duration", eb->stop - eb->start);
- // TODO: the above can be removed
/* Title/description */
if ((s = epg_broadcast_get_title(eb, lang)))
htsmsg_add_str(m, "title", s);
-#if TODO
if ((s = epg_broadcast_get_subtitle(eb, lang)))
htsmsg_add_str(m, "subtitle", s);
-#endif
if ((s = epg_broadcast_get_summary(eb, lang)))
htsmsg_add_str(m, "summary", s);
if ((s = epg_broadcast_get_description(eb, lang)))
/* Image */
if (ee->image)
htsmsg_add_str(m, "image", ee->image);
+
+ /* Rating */
+ if (ee->star_rating)
+ htsmsg_add_u32(m, "starRating", ee->star_rating);
+ if (ee->age_rating)
+ htsmsg_add_u32(m, "ageRating", ee->age_rating);
+
+ /* Content Type */
+ m2 = NULL;
+ LIST_FOREACH(eg, &ee->genre, link) {
+ if (m2 == NULL)
+ m2 = htsmsg_create_list();
+ htsmsg_add_u32(m2, NULL, eg->code);
+ }
+ if (m2)
+ htsmsg_add_msg(m, "genre", m2);
}
-
+
/* Recording */
- if ((de = dvr_entry_find_by_event(eb)))
- htsmsg_add_str(m, "dvrId", idnode_uuid_as_str(&de->de_id));
+ if ((de = dvr_entry_find_by_event(eb))) {
+ htsmsg_add_str(m, "dvrUuid", idnode_uuid_as_str(&de->de_id));
+ htsmsg_add_str(m, "dvrState", dvr_entry_schedstatus(de));
+ }
/* Next event */
if ((eb = epg_broadcast_get_next(eb)))
return m;
}
+static void
+api_epg_filter_set_str
+ ( epg_filter_str_t *f, const char *str, int comp )
+{
+ f->str = strdup(str);
+ f->comp = comp;
+}
+
+static void
+api_epg_filter_add_str
+ ( epg_query_t *eq, const char *k, const char *v, int comp )
+{
+ if (!strcmp(k, "channelName"))
+ api_epg_filter_set_str(&eq->channel_name, v, comp);
+ else if (!strcmp(k, "title"))
+ api_epg_filter_set_str(&eq->title, v, comp);
+ else if (!strcmp(k, "subtitle"))
+ api_epg_filter_set_str(&eq->subtitle, v, comp);
+ else if (!strcmp(k, "summary"))
+ api_epg_filter_set_str(&eq->summary, v, comp);
+ else if (!strcmp(k, "description"))
+ api_epg_filter_set_str(&eq->description, v, comp);
+}
+
+static void
+api_epg_filter_set_num
+ ( epg_filter_num_t *f, int64_t v1, int64_t v2, int comp )
+{
+ /* Range? */
+ if (f->comp == EC_LT && comp == EC_GT) {
+ f->val2 = f->val1;
+ f->val1 = v1;
+ f->comp = EC_RG;
+ return;
+ }
+ if (f->comp == EC_GT && comp == EC_LT) {
+ f->val2 = v1;
+ f->comp = EC_RG;
+ return;
+ }
+ f->val1 = v1;
+ f->val2 = v2;
+ f->comp = comp;
+}
+
+static void
+api_epg_filter_add_num
+ ( epg_query_t *eq, const char *k, int64_t v1, int64_t v2, int comp )
+{
+ if (!strcmp(k, "start"))
+ api_epg_filter_set_num(&eq->start, v1, v2, comp);
+ else if (!strcmp(k, "stop"))
+ api_epg_filter_set_num(&eq->stop, v1, v2, comp);
+ else if (!strcmp(k, "duration"))
+ api_epg_filter_set_num(&eq->duration, v1, v2, comp);
+ else if (!strcmp(k, "episode"))
+ api_epg_filter_set_num(&eq->episode, v1, v2, comp);
+ else if (!strcmp(k, "stars"))
+ api_epg_filter_set_num(&eq->episode, v1, v2, comp);
+ else if (!strcmp(k, "age"))
+ api_epg_filter_set_num(&eq->age, v1, v2, comp);
+}
+
+static struct strtab sortcmptab[] = {
+ { "start", ESK_START },
+ { "stop", ESK_STOP },
+ { "duration", ESK_DURATION },
+ { "title", ESK_TITLE },
+ { "subtitle", ESK_SUBTITLE },
+ { "summary", ESK_SUMMARY },
+ { "description", ESK_DESCRIPTION },
+ { "channelName", ESK_CHANNEL },
+ { "channelNumber", ESK_CHANNEL_NUM },
+ { "starRating", ESK_STARS },
+ { "ageRating", ESK_AGE },
+ { "genre", ESK_GENRE }
+};
+
+static struct strtab filtcmptab[] = {
+ { "gt", EC_GT },
+ { "lt", EC_LT },
+ { "eq", EC_EQ },
+ { "regex", EC_RE },
+ { "range", EC_RG }
+};
+
+static int64_t
+api_epg_decode_channel_num ( const char *s )
+{
+ int64_t v = atol(s);
+ const char *s1 = strchr(s, '.');
+ if (s1)
+ v += atol(s1 + 1) % CHANNEL_SPLIT;
+ return v;
+}
+
static int
api_epg_grid
( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
{
int i;
- epg_query_result_t eqr;
- const char *ch, *tag, *title, *lang/*, *genre*/;
- uint32_t start, limit, end;
- htsmsg_t *l = NULL, *e;
- int min_duration;
- int max_duration;
+ epg_query_t eq;
+ const char *lang, *str;
+ uint32_t start, limit, end, genre;
+ int64_t duration_min, duration_max;
+ htsmsg_field_t *f, *f2;
+ htsmsg_t *l = NULL, *e, *filter;
*resp = htsmsg_create_map();
- /* Query params */
- ch = htsmsg_get_str(args, "channel");
- tag = htsmsg_get_str(args, "tag");
- //genre = htsmsg_get_str(args, "genre");
- title = htsmsg_get_str(args, "title");
- lang = htsmsg_get_str(args, "lang");
- // TODO: support multiple tag/genre/channel?
+ memset(&eq, 0, sizeof(eq));
+
+ lang = htsmsg_get_str(args, "lang");
+ eq.lang = lang ? strdup(lang) : NULL;
+
+ str = htsmsg_get_str(args, "title");
+ if (str)
+ eq.stitle = strdup(str);
+ str = htsmsg_get_str(args, "channel");
+ if (str)
+ eq.channel = strdup(str);
+ str = htsmsg_get_str(args, "channelTag");
+ if (str)
+ eq.channel_tag = strdup(str);
+
+ duration_min = -1;
+ duration_max = -1;
+ htsmsg_get_s64(args, "durationMin", &duration_min);
+ htsmsg_get_s64(args, "durationMax", &duration_max);
+ if (duration_min > 0 || duration_max > 0) {
+ eq.duration.comp = EC_RG;
+ eq.duration.val1 = duration_min < 0 ? 0 : duration_min;
+ eq.duration.val2 = duration_max < 0 ? 0 : duration_max;
+ }
+
+ if (!htsmsg_get_u32(args, "contentType", &genre)) {
+ eq.genre = eq.genre_static;
+ eq.genre[0] = genre;
+ eq.genre_count = 1;
+ }
+
+ /* Filter params */
+ if ((filter = htsmsg_get_list(args, "filter"))) {
+ HTSMSG_FOREACH(f, filter) {
+ const char *k, *t, *v;
+ int comp;
+ if (!(e = htsmsg_get_map_by_field(f))) continue;
+ if (!(k = htsmsg_get_str(e, "field"))) continue;
+ if (!(t = htsmsg_get_str(e, "type"))) continue;
+ comp = str2val(htsmsg_get_str(e, "comparison") ?: "", filtcmptab);
+ if (comp == -1) comp = EC_EQ;
+ if (!strcmp(k, "channelNumber")) {
+ if (!strcmp(t, "numeric")) {
+ f2 = htsmsg_field_find(e, "value");
+ if (f2) {
+ int64_t v1, v2 = 0;
+ if (f2->hmf_type == HMF_STR) {
+ const char *s = htsmsg_field_get_str(f2);
+ const char *z = s ? strchr(s, ';') : NULL;
+ if (s) {
+ v1 = api_epg_decode_channel_num(s);
+ if (z)
+ v2 = api_epg_decode_channel_num(z);
+ api_epg_filter_set_num(&eq.channel_num, v1, v2, comp);
+ }
+ } else {
+ if (!htsmsg_field_get_s64(f2, &v1)) {
+ if (v1 < CHANNEL_SPLIT)
+ v1 *= CHANNEL_SPLIT;
+ api_epg_filter_set_num(&eq.channel_num, v1, 0, comp);
+ }
+ }
+ }
+ }
+ } else if (!strcmp(k, "genre")) {
+ if (!strcmp(t, "numeric")) {
+ f2 = htsmsg_field_find(e, "value");
+ if (f2) {
+ int64_t v;
+ if (f2->hmf_type == HMF_STR) {
+ htsmsg_t *z = api_epg_get_list(htsmsg_field_get_str(f2));
+ if (z) {
+ htsmsg_field_t *f3;
+ uint32_t count = 0;
+ HTSMSG_FOREACH(f3, z)
+ count++;
+ if (ARRAY_SIZE(eq.genre_static) > count)
+ eq.genre = malloc(sizeof(eq.genre[0]) * count);
+ else
+ eq.genre = eq.genre_static;
+ HTSMSG_FOREACH(f3, z)
+ if (!htsmsg_field_get_s64(f3, &v))
+ eq.genre[eq.genre_count++] = v;
+ }
+ } else {
+ if (!htsmsg_field_get_s64(f2, &v)) {
+ eq.genre_count = 1;
+ eq.genre = eq.genre_static;
+ eq.genre[0] = v;
+ }
+ }
+ }
+ }
+ } else if (!strcmp(t, "string")) {
+ if ((v = htsmsg_get_str(e, "value")))
+ api_epg_filter_add_str(&eq, k, v, EC_RE);
+ } else if (!strcmp(t, "numeric")) {
+ f2 = htsmsg_field_find(e, "value");
+ if (f2) {
+ int64_t v1, v2 = 0;
+ if (f2->hmf_type == HMF_STR) {
+ const char *z = htsmsg_field_get_str(f2);
+ if (z) {
+ const char *z2 = strchr(z, ';');
+ if (z2)
+ v2 = strtoll(z2 + 1, NULL, 0);
+ }
+ v1 = strtoll(z, NULL, 0);
+ api_epg_filter_add_num(&eq, k, v1, v2, comp);
+ } else {
+ if (!htsmsg_field_get_s64(f2, &v1))
+ api_epg_filter_add_num(&eq, k, v1, v2, comp);
+ }
+ }
+ }
+ }
+ }
- min_duration = htsmsg_get_u32_or_default(args, "minduration", 0);
- max_duration = htsmsg_get_u32_or_default(args, "maxduration", INT_MAX);
+ /* Sort */
+ if ((str = htsmsg_get_str(args, "sort"))) {
+ int skey = str2val(str, sortcmptab);
+ if (skey >= 0) {
+ eq.sort_key = skey;
+ if ((str = htsmsg_get_str(args, "dir")) && !strcasecmp(str, "DESC"))
+ eq.sort_dir = IS_DSC;
+ else
+ eq.sort_dir = IS_ASC;
+ }
+ } /* else.. keep default start time ascending sorting */
/* Pagination settings */
start = htsmsg_get_u32_or_default(args, "start", 0);
/* Query the EPG */
pthread_mutex_lock(&global_lock);
- epg_query(&eqr, ch, tag, NULL, /*genre,*/ title, lang, min_duration, max_duration);
- epg_query_sort(&eqr);
- // TODO: optional sorting
+ epg_query(&eq);
/* Build response */
- start = MIN(eqr.eqr_entries, start);
- end = MIN(eqr.eqr_entries, start + limit);
+ start = MIN(eq.entries, start);
+ end = MIN(eq.entries, start + limit);
+ l = htsmsg_create_list();
for (i = start; i < end; i++) {
- if (!(e = api_epg_entry(eqr.eqr_array[i], lang))) continue;
- if (!l) l = htsmsg_create_list();
+ if (!(e = api_epg_entry(eq.result[i], lang))) continue;
htsmsg_add_msg(l, NULL, e);
}
+ pthread_mutex_unlock(&global_lock);
+
+ epg_query_free(&eq);
+
+ /* Build response */
+ *resp = htsmsg_create_map();
+ htsmsg_add_u32(*resp, "totalCount", eq.entries);
+ htsmsg_add_msg(*resp, "entries", l);
+
+ return 0;
+}
+
+static void
+api_epg_episode_broadcasts
+ ( htsmsg_t *l, const char *lang, epg_episode_t *ep,
+ uint32_t *entries, epg_broadcast_t *ebc_skip )
+{
+ epg_broadcast_t *ebc;
+ channel_t *ch;
+ htsmsg_t *m;
+
+ LIST_FOREACH(ebc, &ep->broadcasts, ep_link) {
+ ch = ebc->channel;
+ if (ch == NULL) continue;
+ if (ebc == ebc_skip) continue;
+ m = api_epg_entry(ebc, lang);
+ htsmsg_add_msg(l, NULL, m);
+ (*entries)++;
+ }
+}
+
+static int
+api_epg_alternative
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ uint32_t id, entries = 0;
+ htsmsg_t *l = htsmsg_create_list();
+ epg_broadcast_t *e;
+ const char *lang = htsmsg_get_str(args, "lang");
+ if (!htsmsg_get_u32(args, "eventId", &id))
+ return -EINVAL;
+
+ /* Main Job */
+ pthread_mutex_lock(&global_lock);
+ e = epg_broadcast_find_by_id(id, NULL);
+ if (e && e->episode)
+ api_epg_episode_broadcasts(l, lang, e->episode, &entries, e);
pthread_mutex_unlock(&global_lock);
/* Build response */
- htsmsg_add_u32(*resp, "totalCount", eqr.eqr_entries);
- if (l)
- htsmsg_add_msg(*resp, "events", l);
+ htsmsg_add_u32(*resp, "totalCount", entries);
+ htsmsg_add_msg(*resp, "entries", l);
+
+ return 0;
+}
+static int
+api_epg_related
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ uint32_t id, entries = 0;
+ htsmsg_t *l = htsmsg_create_list();
+ epg_broadcast_t *e;
+ epg_episode_t *ep, *ep2;
+ const char *lang = htsmsg_get_str(args, "lang");
+ if (!htsmsg_get_u32(args, "eventId", &id))
+ return -EINVAL;
+
+ /* Main Job */
+ pthread_mutex_lock(&global_lock);
+ e = epg_broadcast_find_by_id(id, NULL);
+ ep = e ? e->episode : NULL;
+ if (ep && ep->brand) {
+ LIST_FOREACH(ep2, &ep->brand->episodes, blink) {
+ if (ep2 == ep) continue;
+ if (!ep2->title) continue;
+ api_epg_episode_broadcasts(l, lang, ep2, &entries, e);
+ entries++;
+ }
+ } else if (ep && ep->season) {
+ LIST_FOREACH(ep2, &ep->season->episodes, slink) {
+ if (ep2 == ep) continue;
+ if (!ep2->title) continue;
+ api_epg_episode_broadcasts(l, lang, ep2, &entries, e);
+ }
+ }
+ pthread_mutex_unlock(&global_lock);
+
+ /* Build response */
+ htsmsg_add_u32(*resp, "totalCount", entries);
+ htsmsg_add_msg(*resp, "entries", l);
+
+ return 0;
+}
+
+static int
+api_epg_brand_list(access_t *perm, void *opaque, const char *op,
+ htsmsg_t *args, htsmsg_t **resp)
+{
+ htsmsg_t *array;
+
+ *resp = htsmsg_create_map();
+ pthread_mutex_lock(&global_lock);
+ array = epg_brand_list();
+ pthread_mutex_unlock(&global_lock);
+ htsmsg_add_msg(*resp, "entries", array);
return 0;
}
htsmsg_t *args, htsmsg_t **resp)
{
htsmsg_t *array;
+ int full = 0;
+
+ htsmsg_get_bool(args, "full", &full);
*resp = htsmsg_create_map();
- array = epg_genres_list_all(1, 0);
+ array = epg_genres_list_all(full ? 0 : 1, 0);
htsmsg_add_msg(*resp, "entries", array);
return 0;
}
-
void api_epg_init ( void )
{
static api_hook_t ah[] = {
- { "epg/data/grid", ACCESS_ANONYMOUS, api_epg_grid, NULL },
- { "epg/content_type/list", ACCESS_ANONYMOUS, api_epg_content_type_list, NULL },
+ { "epg/events/grid", ACCESS_ANONYMOUS, api_epg_grid, NULL },
+ { "epg/events/alternative", ACCESS_ANONYMOUS, api_epg_alternative, NULL },
+ { "epg/events/related", ACCESS_ANONYMOUS, api_epg_related, NULL },
+ { "epg/brand/list", ACCESS_ANONYMOUS, api_epg_brand_list, NULL },
+ { "epg/content_type/list", ACCESS_ANONYMOUS, api_epg_content_type_list, NULL },
{ NULL },
};
/* Sort */
if ((str = htsmsg_get_str(args, "sort"))) {
conf->sort.key = str;
- if ((str = htsmsg_get_str(args, "dir")) && !strcmp(str, "DESC"))
+ if ((str = htsmsg_get_str(args, "dir")) && !strcasecmp(str, "DESC"))
conf->sort.dir = IS_DSC;
else
conf->sort.dir = IS_ASC;
channel_find_by_name ( const char *name )
{
channel_t *ch;
+ if (name == NULL)
+ return NULL;
CHANNEL_FOREACH(ch)
if (!strcmp(channel_get_name(ch), name))
break;
{
channel_tag_t *ct;
+ if (name == NULL)
+ return NULL;
+
TAILQ_FOREACH(ct, &channel_tags, ct_link)
if(!strcasecmp(ct->ct_name, name))
return ct;
return epg_episode_get_title(b->episode, lang);
}
+const char *epg_broadcast_get_subtitle ( epg_broadcast_t *b, const char *lang )
+{
+ if (!b || !b->episode) return NULL;
+ return epg_episode_get_subtitle(b->episode, lang);
+}
+
const char *epg_broadcast_get_summary ( epg_broadcast_t *b, const char *lang )
{
if (!b || !b->summary) return NULL;
for (j = 0; j < (major_only ? 1 : 16); j++) {
if (_epg_genre_names[i][j]) {
e = htsmsg_create_map();
- htsmsg_add_u32(e, "key", major_only ? i : (i << 4 | j));
+ htsmsg_add_u32(e, "key", (i << 4) | (major_only ? 0 : j));
htsmsg_add_str(e, "val", _epg_genre_names[i][j]);
// TODO: use major_prefix
htsmsg_add_msg(m, NULL, e);
* Querying
* *************************************************************************/
-static void _eqr_add
- ( epg_query_result_t *eqr, epg_broadcast_t *e,
- epg_genre_t *genre, regex_t *preg, time_t start, const char *lang, int min_duration, int max_duration )
-{
- const char *title;
- double duration;
-
- /* Ignore */
- if ( e->stop < start ) return;
- if ( !(title = epg_episode_get_title(e->episode, lang)) ) return;
- if ( genre && !epg_genre_list_contains(&e->episode->genre, genre, 1) ) return;
- if ( preg && regexec(preg, title, 0, NULL, 0)) return;
-
- duration = difftime(e->stop,e->start);
- if ( duration < min_duration || duration > max_duration ) return;
+static inline int
+_eq_comp_num ( epg_filter_num_t *f, int64_t val )
+{
+ switch (f->comp) {
+ case EC_EQ: return val != f->val1;
+ case EC_LT: return val > f->val1;
+ case EC_GT: return val < f->val1;
+ case EC_RG: return val < f->val1 || val > f->val2;
+ default: return 0;
+ }
+}
+
+static inline int
+_eq_comp_str ( epg_filter_str_t *f, const char *str )
+{
+ switch (f->comp) {
+ case EC_EQ: return strcmp(str, f->str);
+ case EC_LT: return strcmp(str, f->str) > 0;
+ case EC_GT: return strcmp(str, f->str) < 0;
+ case EC_IN: return strstr(str, f->str) != NULL;
+ case EC_RE: return regexec(&f->re, str, 0, NULL, 0) != 0;
+ default: return 0;
+ }
+}
+
+static void
+_eq_add ( epg_query_t *eq, epg_broadcast_t *e )
+{
+ const char *s, *lang = eq->lang;
+ epg_episode_t *ep;
+
+ /* Filtering */
+ if (e->stop < dispatch_clock) return;
+ if (_eq_comp_num(&eq->start, e->start)) return;
+ if (_eq_comp_num(&eq->stop, e->stop)) return;
+ if (eq->duration.comp != EC_NO) {
+ int64_t duration = (int64_t)e->stop - (int64_t)e->start;
+ if (_eq_comp_num(&eq->duration, duration)) return;
+ }
+ ep = e->episode;
+ if (eq->stars.comp != EC_NO) {
+ if (e == NULL) return;
+ if (_eq_comp_num(&eq->stars, ep->star_rating)) return;
+ }
+ if (eq->age.comp != EC_NO) {
+ if (e == NULL) return;
+ if (_eq_comp_num(&eq->age, ep->age_rating)) return;
+ }
+ if (eq->channel_num.comp != EC_NO)
+ if (_eq_comp_num(&eq->channel_num, channel_get_number(e->channel))) return;
+ if (eq->channel_name.comp != EC_NO)
+ if (_eq_comp_str(&eq->channel_name, channel_get_name(e->channel))) return;
+ if (eq->genre_count) {
+ epg_genre_t genre;
+ uint32_t i, r = 0;
+ for (i = 0; i < eq->genre_count; i++) {
+ genre.code = eq->genre[i];
+ if (genre.code == 0) continue;
+ if (epg_genre_list_contains(&e->episode->genre, &genre, 1)) r++;
+ }
+ if (!r) return;
+ }
+ if (eq->title.comp != EC_NO || eq->stitle) {
+ if ((s = epg_episode_get_title(ep, lang)) == NULL) return;
+ if (eq->stitle)
+ if (regexec(&eq->stitle_re, s, 0, NULL, 0)) return;
+ if (_eq_comp_str(&eq->title, s)) return;
+ }
+ if (eq->subtitle.comp != EC_NO) {
+ if ((s = epg_episode_get_subtitle(ep, lang)) == NULL) return;
+ if (_eq_comp_str(&eq->subtitle, s)) return;
+ }
+ if (eq->summary.comp != EC_NO) {
+ if ((s = epg_episode_get_summary(ep, lang)) == NULL) return;
+ if (_eq_comp_str(&eq->summary, s)) return;
+ }
+ if (eq->description.comp != EC_NO) {
+ if ((s = epg_episode_get_description(ep, lang)) == NULL) return;
+ if (_eq_comp_str(&eq->description, s)) return;
+ }
/* More space */
- if ( eqr->eqr_entries == eqr->eqr_alloced ) {
- eqr->eqr_alloced = MAX(100, eqr->eqr_alloced * 2);
- eqr->eqr_array = realloc(eqr->eqr_array,
- eqr->eqr_alloced * sizeof(epg_broadcast_t));
+ if (eq->entries == eq->allocated) {
+ eq->allocated = MAX(100, eq->allocated + 100);
+ eq->result = realloc(eq->result, eq->allocated * sizeof(epg_broadcast_t *));
}
-
+
/* Store */
- eqr->eqr_array[eqr->eqr_entries++] = e;
+ eq->result[eq->entries++] = e;
}
-static void _eqr_add_channel
- ( epg_query_result_t *eqr, channel_t *ch, epg_genre_t *genre,
- regex_t *preg, time_t start, const char *lang, int min_duration, int max_duration )
+static void
+_eq_add_channel ( epg_query_t *eq, channel_t *ch )
{
epg_broadcast_t *ebc;
RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) {
- if ( ebc->episode ) _eqr_add(eqr, ebc, genre, preg, start, lang, min_duration, max_duration);
+ if (ebc->episode)
+ _eq_add(eq, ebc);
}
}
-void epg_query0
- ( epg_query_result_t *eqr, channel_t *channel, channel_tag_t *tag,
- epg_genre_t *genre, const char *title, const char *lang, int min_duration, int max_duration )
+static int
+_eq_init_str( epg_filter_str_t *f )
{
- time_t now;
- channel_tag_mapping_t *ctm;
- regex_t preg0, *preg;
- time(&now);
+ if (f->comp != EC_RE) return 0;
+ return regcomp(&f->re, f->str, REG_ICASE | REG_EXTENDED | REG_NOSUB);
+}
- /* Clear (just incase) */
- memset(eqr, 0, sizeof(epg_query_result_t));
+static void
+_eq_done_str( epg_filter_str_t *f )
+{
+ if (f->comp == EC_RE)
+ regfree(&f->re);
+ free(f->str);
+ f->str = NULL;
+}
- /* Setup exp */
- if ( title ) {
- if (regcomp(&preg0, title, REG_ICASE | REG_EXTENDED | REG_NOSUB) )
- return;
- preg = &preg0;
- } else {
- preg = NULL;
+static int _epg_sort_start_ascending ( const void *a, const void *b, void *eq )
+{
+ return (*(epg_broadcast_t**)a)->start - (*(epg_broadcast_t**)b)->start;
+}
+
+static int _epg_sort_start_descending ( const void *a, const void *b, void *eq )
+{
+ return (*(epg_broadcast_t**)b)->start - (*(epg_broadcast_t**)a)->start;
+}
+
+static int _epg_sort_stop_ascending ( const void *a, const void *b, void *eq )
+{
+ return (*(epg_broadcast_t**)a)->stop - (*(epg_broadcast_t**)b)->stop;
+}
+
+static int _epg_sort_stop_descending ( const void *a, const void *b, void *eq )
+{
+ return (*(epg_broadcast_t**)b)->stop - (*(epg_broadcast_t**)a)->stop;
+}
+
+static inline int64_t _epg_sort_duration( const epg_broadcast_t *b )
+{
+ return b->stop - b->start;
+}
+
+static int _epg_sort_duration_ascending ( const void *a, const void *b, void *eq )
+{
+ return _epg_sort_duration(*(epg_broadcast_t**)a) - _epg_sort_duration(*(epg_broadcast_t**)b);
+}
+
+static int _epg_sort_duration_descending ( const void *a, const void *b, void *eq )
+{
+ return _epg_sort_duration(*(epg_broadcast_t**)b) - _epg_sort_duration(*(epg_broadcast_t**)a);
+}
+
+static int _epg_sort_title_ascending ( const void *a, const void *b, void *eq )
+{
+ const char *s1 = epg_broadcast_get_title(*(epg_broadcast_t**)a, ((epg_query_t *)eq)->lang);
+ const char *s2 = epg_broadcast_get_title(*(epg_broadcast_t**)b, ((epg_query_t *)eq)->lang);
+ if (s1 == NULL && s2) return 1;
+ if (s1 && s2 == NULL) return -1;
+ return strcmp(s1, s2);
+}
+
+static int _epg_sort_title_descending ( const void *a, const void *b, void *eq )
+{
+ return _epg_sort_title_ascending(a, b, eq) * -1;
+}
+
+static int _epg_sort_subtitle_ascending ( const void *a, const void *b, void *eq )
+{
+ const char *s1 = epg_broadcast_get_subtitle(*(epg_broadcast_t**)a, ((epg_query_t *)eq)->lang);
+ const char *s2 = epg_broadcast_get_subtitle(*(epg_broadcast_t**)b, ((epg_query_t *)eq)->lang);
+ if (s1 == NULL && s2 == NULL) return 0;
+ if (s1 == NULL && s2) return 1;
+ if (s1 && s2 == NULL) return -1;
+ return strcmp(s1, s2);
+}
+
+static int _epg_sort_subtitle_descending ( const void *a, const void *b, void *eq )
+{
+ return _epg_sort_subtitle_ascending(a, b, eq) * -1;
+}
+
+static int _epg_sort_summary_ascending ( const void *a, const void *b, void *eq )
+{
+ const char *s1 = epg_broadcast_get_summary(*(epg_broadcast_t**)a, ((epg_query_t *)eq)->lang);
+ const char *s2 = epg_broadcast_get_summary(*(epg_broadcast_t**)b, ((epg_query_t *)eq)->lang);
+ if (s1 == NULL && s2 == NULL) return 0;
+ if (s1 == NULL && s2) return 1;
+ if (s1 && s2 == NULL) return -1;
+ return strcmp(s1, s2);
+}
+
+static int _epg_sort_summary_descending ( const void *a, const void *b, void *eq )
+{
+ return _epg_sort_summary_ascending(a, b, eq) * -1;
+}
+
+static int _epg_sort_description_ascending ( const void *a, const void *b, void *eq )
+{
+ const char *s1 = epg_broadcast_get_description(*(epg_broadcast_t**)a, ((epg_query_t *)eq)->lang);
+ const char *s2 = epg_broadcast_get_description(*(epg_broadcast_t**)b, ((epg_query_t *)eq)->lang);
+ if (s1 == NULL && s2 == NULL) return 0;
+ if (s1 == NULL && s2) return 1;
+ if (s1 && s2 == NULL) return -1;
+ return strcmp(s1, s2);
+}
+
+static int _epg_sort_description_descending ( const void *a, const void *b, void *eq )
+{
+ return _epg_sort_description_ascending(a, b, eq) * -1;
+}
+
+static int _epg_sort_channel_ascending ( const void *a, const void *b, void *eq )
+{
+ char *s1 = strdup(channel_get_name((*(epg_broadcast_t**)a)->channel));
+ char *s2 = strdup(channel_get_name((*(epg_broadcast_t**)b)->channel));
+ int r = strcmp(s1, s2);
+ free(s2);
+ free(s1);
+ return r;
+}
+
+static int _epg_sort_channel_descending ( const void *a, const void *b, void *eq )
+{
+ return _epg_sort_description_ascending(a, b, eq) * -1;
+}
+
+static int _epg_sort_channel_num_ascending ( const void *a, const void *b, void *eq )
+{
+ int64_t v1 = channel_get_number((*(epg_broadcast_t**)a)->channel);
+ int64_t v2 = channel_get_number((*(epg_broadcast_t**)b)->channel);
+ return v1 - v2;
+}
+
+static int _epg_sort_channel_num_descending ( const void *a, const void *b, void *eq )
+{
+ int64_t v1 = channel_get_number((*(epg_broadcast_t**)a)->channel);
+ int64_t v2 = channel_get_number((*(epg_broadcast_t**)b)->channel);
+ return v2 - v1;
+}
+
+static int _epg_sort_stars_ascending ( const void *a, const void *b, void *eq )
+{
+ return (*(epg_broadcast_t**)a)->episode->star_rating - (*(epg_broadcast_t**)b)->episode->star_rating;
+}
+
+static int _epg_sort_stars_descending ( const void *a, const void *b, void *eq )
+{
+ return (*(epg_broadcast_t**)b)->episode->star_rating - (*(epg_broadcast_t**)a)->episode->star_rating;
+}
+
+static int _epg_sort_age_ascending ( const void *a, const void *b, void *eq )
+{
+ return (*(epg_broadcast_t**)a)->episode->age_rating - (*(epg_broadcast_t**)b)->episode->age_rating;
+}
+
+static int _epg_sort_age_descending ( const void *a, const void *b, void *eq )
+{
+ return (*(epg_broadcast_t**)b)->episode->age_rating - (*(epg_broadcast_t**)a)->episode->age_rating;
+}
+
+static uint64_t _epg_sort_genre_hash( epg_episode_t *ep )
+{
+ uint64_t h = 0, t;
+ epg_genre_t *g;
+
+ LIST_FOREACH(g, &ep->genre, link) {
+ t = h >> 28;
+ h <<= 8;
+ h += (uint64_t)g->code + t;
}
-
+ return h;
+}
+
+static int _epg_sort_genre_ascending ( const void *a, const void *b, void *eq )
+{
+ uint64_t v1 = _epg_sort_genre_hash((*(epg_broadcast_t**)a)->episode);
+ uint64_t v2 = _epg_sort_genre_hash((*(epg_broadcast_t**)b)->episode);
+ return v1 - v2;
+}
+
+static int _epg_sort_genre_descending ( const void *a, const void *b, void *eq )
+{
+ return _epg_sort_genre_ascending(a, b, eq) * -1;
+}
+
+epg_broadcast_t **
+epg_query ( epg_query_t *eq )
+{
+ channel_t *channel;
+ channel_tag_t *tag;
+ int (*fcn)(const void *, const void *, void *) = NULL;
+
+ /* Setup exp */
+ if (_eq_init_str(&eq->title)) goto fin;
+ if (_eq_init_str(&eq->subtitle)) goto fin;
+ if (_eq_init_str(&eq->summary)) goto fin;
+ if (_eq_init_str(&eq->description)) goto fin;
+ if (_eq_init_str(&eq->channel_name)) goto fin;
+
+ if (eq->stitle)
+ if (regcomp(&eq->stitle_re, eq->stitle, REG_ICASE | REG_EXTENDED | REG_NOSUB))
+ goto fin;
+
+ channel = channel_find_by_uuid(eq->channel) ?:
+ channel_find_by_name(eq->channel);
+
+ tag = channel_tag_find_by_uuid(eq->channel_tag) ?:
+ channel_tag_find_by_name(eq->channel_tag, 0);
+
/* Single channel */
- if (channel && !tag) {
- _eqr_add_channel(eqr, channel, genre, preg, now, lang, min_duration, max_duration);
+ if (channel && tag == NULL) {
+ _eq_add_channel(eq, channel);
/* Tag based */
- } else if ( tag ) {
+ } else if (tag) {
+ channel_tag_mapping_t *ctm;
LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
if(channel == NULL || ctm->ctm_channel == channel)
- _eqr_add_channel(eqr, ctm->ctm_channel, genre, preg, now, lang, min_duration, max_duration);
+ _eq_add_channel(eq, ctm->ctm_channel);
}
/* All channels */
} else {
CHANNEL_FOREACH(channel)
- _eqr_add_channel(eqr, channel, genre, preg, now, lang, min_duration, max_duration);
+ _eq_add_channel(eq, channel);
+ }
+
+ switch (eq->sort_dir) {
+ case ES_ASC:
+ switch (eq->sort_key) {
+ case ESK_START: fcn = _epg_sort_start_ascending; break;
+ case ESK_STOP: fcn = _epg_sort_stop_ascending; break;
+ case ESK_DURATION: fcn = _epg_sort_duration_ascending; break;
+ case ESK_TITLE: fcn = _epg_sort_title_ascending; break;
+ case ESK_SUBTITLE: fcn = _epg_sort_subtitle_ascending; break;
+ case ESK_SUMMARY: fcn = _epg_sort_summary_ascending; break;
+ case ESK_DESCRIPTION: fcn = _epg_sort_description_ascending; break;
+ case ESK_CHANNEL: fcn = _epg_sort_channel_ascending; break;
+ case ESK_CHANNEL_NUM: fcn = _epg_sort_channel_num_ascending; break;
+ case ESK_STARS: fcn = _epg_sort_stars_ascending; break;
+ case ESK_AGE: fcn = _epg_sort_age_ascending; break;
+ case ESK_GENRE: fcn = _epg_sort_genre_ascending; break;
+ }
+ break;
+ case ES_DSC:
+ switch (eq->sort_key) {
+ case ESK_START: fcn = _epg_sort_start_descending; break;
+ case ESK_STOP: fcn = _epg_sort_stop_descending; break;
+ case ESK_DURATION: fcn = _epg_sort_duration_descending; break;
+ case ESK_TITLE: fcn = _epg_sort_title_descending; break;
+ case ESK_SUBTITLE: fcn = _epg_sort_subtitle_descending; break;
+ case ESK_SUMMARY: fcn = _epg_sort_summary_descending; break;
+ case ESK_DESCRIPTION: fcn = _epg_sort_description_descending; break;
+ case ESK_CHANNEL: fcn = _epg_sort_channel_descending; break;
+ case ESK_CHANNEL_NUM: fcn = _epg_sort_channel_num_descending; break;
+ case ESK_STARS: fcn = _epg_sort_stars_descending; break;
+ case ESK_AGE: fcn = _epg_sort_age_descending; break;
+ case ESK_GENRE: fcn = _epg_sort_genre_descending; break;
+ }
+ break;
}
- if (preg) regfree(preg);
- return;
-}
+ tvh_qsort_r(eq->result, eq->entries, sizeof(epg_broadcast_t *), fcn, eq);
-void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
- epg_genre_t *genre, const char *title, const char *lang, int min_duration, int max_duration)
-{
- channel_t *ch = channel ? channel_find(channel) : NULL;
- channel_tag_t *ct = tag ? channel_tag_find_by_uuid(tag) : NULL;
+fin:
+ _eq_done_str(&eq->title);
+ _eq_done_str(&eq->subtitle);
+ _eq_done_str(&eq->summary);
+ _eq_done_str(&eq->description);
+ _eq_done_str(&eq->channel_name);
- epg_query0(eqr, ch, ct, genre, title, lang, min_duration, max_duration);
-}
+ free(eq->lang); eq->lang = NULL;
+ free(eq->channel); eq->channel = NULL;
+ free(eq->channel_tag); eq->channel_tag = NULL;
+ free(eq->stitle); eq->stitle = NULL;
+ if (eq->genre != eq->genre_static)
+ free(eq->genre);
+ eq->genre = NULL;
-void epg_query_free(epg_query_result_t *eqr)
-{
- free(eqr->eqr_array);
+ return eq->result;
}
-static int _epg_sort_start_ascending ( const void *a, const void *b )
+void epg_query_free(epg_query_t *eq)
{
- return (*(epg_broadcast_t**)a)->start - (*(epg_broadcast_t**)b)->start;
+ free(eq->result); eq->result = NULL;
}
-void epg_query_sort(epg_query_result_t *eqr)
-{
- qsort(eqr->eqr_array, eqr->eqr_entries, sizeof(epg_broadcast_t*),
- _epg_sort_start_ascending);
-}
/* **************************************************************************
* Miscellaneous
#ifndef EPG_H
#define EPG_H
+#include <regex.h>
#include "settings.h"
#include "lang_str.h"
( epg_broadcast_t *b, int create, int *save );
const char *epg_broadcast_get_title
( epg_broadcast_t *b, const char *lang );
+const char *epg_broadcast_get_subtitle
+ ( epg_broadcast_t *b, const char *lang );
const char *epg_broadcast_get_summary
( epg_broadcast_t *b, const char *lang );
const char *epg_broadcast_get_description
* Querying
* ***********************************************************************/
-/*
- * Query result
- */
-typedef struct epg_query_result {
- epg_broadcast_t **eqr_array;
- int eqr_entries;
- int eqr_alloced;
-} epg_query_result_t;
-
-void epg_query_free(epg_query_result_t *eqr);
-
-/* Sorting */
-// WIBNI: might be useful to have a user defined comparator function
-void epg_query_sort(epg_query_result_t *eqr);
-
-/* Query routines */
-void epg_query0(epg_query_result_t *eqr, struct channel *ch,
- struct channel_tag *ct, epg_genre_t *genre, const char *title,
- const char *lang, int min_duration, int max_duration);
-void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
- epg_genre_t *genre, const char *title, const char *lang, int min_duration, int max_duration);
-
+typedef enum {
+ EC_NO, ///< No filter
+ EC_EQ, ///< Equals
+ EC_LT, ///< LT
+ EC_GT, ///< GT
+ EC_RG, ///< Range
+ EC_IN, ///< contains (STR only)
+ EC_RE, ///< regexp (STR only)
+} epg_comp_t;
+
+typedef struct epg_filter_str {
+ char *str;
+ regex_t re;
+ epg_comp_t comp;
+} epg_filter_str_t;
+
+typedef struct epg_filter_num {
+ int64_t val1;
+ int64_t val2;
+ epg_comp_t comp;
+} epg_filter_num_t;
+
+typedef struct epg_query {
+ /* Configuration */
+ char *lang;
+
+ /* Filter */
+ epg_filter_num_t start;
+ epg_filter_num_t stop;
+ epg_filter_num_t duration;
+ epg_filter_str_t title;
+ epg_filter_str_t subtitle;
+ epg_filter_str_t summary;
+ epg_filter_str_t description;
+ epg_filter_num_t episode;
+ epg_filter_num_t stars;
+ epg_filter_num_t age;
+ epg_filter_str_t channel_name;
+ epg_filter_num_t channel_num;
+ char *stitle;
+ regex_t stitle_re;
+ char *channel;
+ char *channel_tag;
+ uint32_t genre_count;
+ uint8_t *genre;
+ uint8_t genre_static[16];
+
+ enum {
+ ESK_START,
+ ESK_STOP,
+ ESK_DURATION,
+ ESK_TITLE,
+ ESK_SUBTITLE,
+ ESK_SUMMARY,
+ ESK_DESCRIPTION,
+ ESK_CHANNEL,
+ ESK_CHANNEL_NUM,
+ ESK_STARS,
+ ESK_AGE,
+ ESK_GENRE
+ } sort_key;
+ enum {
+ ES_ASC,
+ ES_DSC
+ } sort_dir;
+
+ /* Result */
+ epg_broadcast_t **result;
+ uint32_t entries;
+ uint32_t allocated;
+} epg_query_t;
+
+epg_broadcast_t **epg_query(epg_query_t *eq);
+void epg_query_free(epg_query_t *eq);
/* ************************************************************************
* Setup/Shutdown
uint32_t u32, full;
channel_t *ch = NULL;
channel_tag_t *ct = NULL;
- epg_query_result_t eqr;
- epg_genre_t genre, *eg = NULL;
+ epg_query_t eq;
const char *lang;
int min_duration;
int max_duration;
/* Required */
if( (query = htsmsg_get_str(in, "query")) == NULL )
return htsp_error("Missing argument 'query'");
+
+ memset(&eq, 0, sizeof(eq));
/* Optional */
- if(!(htsmsg_get_u32(in, "channelId", &u32)))
+ if(!(htsmsg_get_u32(in, "channelId", &u32))) {
if (!(ch = channel_find_by_id(u32)))
return htsp_error("Channel does not exist");
- if(!(htsmsg_get_u32(in, "tagId", &u32)))
+ else
+ eq.channel = strdup(idnode_uuid_as_str(&ch->ch_id));
+ }
+ if(!(htsmsg_get_u32(in, "tagId", &u32))) {
if (!(ct = htsp_channel_tag_find_by_identifier(u32)))
return htsp_error("Channel tag does not exist");
+ else
+ eq.channel_tag = strdup(idnode_uuid_as_str(&ct->ct_id));
+ }
if (!htsmsg_get_u32(in, "contentType", &u32)) {
if(htsp->htsp_version < 6) u32 <<= 4;
- genre.code = u32;
- eg = &genre;
+ eq.genre_count = 1;
+ eq.genre = eq.genre_static;
+ eq.genre[0] = u32;
}
lang = htsmsg_get_str(in, "language") ?: htsp->htsp_language;
+ eq.lang = lang ? strdup(lang) : NULL;
full = htsmsg_get_u32_or_default(in, "full", 0);
min_duration = htsmsg_get_u32_or_default(in, "minduration", 0);
max_duration = htsmsg_get_u32_or_default(in, "maxduration", INT_MAX);
+ eq.duration.comp = EC_RG;
+ eq.duration.val1 = min_duration;
+ eq.duration.val2 = max_duration;
tvhtrace("htsp", "min_duration %d and max_duration %d", min_duration, max_duration);
/* Check access */
if (!htsp_user_access_channel(htsp, ch))
return htsp_error("User does not have access");
- //do the query
- epg_query0(&eqr, ch, ct, eg, query, lang, min_duration, max_duration);
+ /* Query */
+ epg_query(&eq);
- // create reply
+ /* Create Reply */
out = htsmsg_create_map();
- if( eqr.eqr_entries ) {
+ if( eq.entries ) {
array = htsmsg_create_list();
- for(i = 0; i < eqr.eqr_entries; ++i) {
+ for(i = 0; i < eq.entries; ++i) {
if (full)
htsmsg_add_msg(array, NULL,
- htsp_build_event(eqr.eqr_array[i], NULL, lang, 0, htsp));
+ htsp_build_event(eq.result[i], NULL, lang, 0, htsp));
else
- htsmsg_add_u32(array, NULL, eqr.eqr_array[i]->id);
+ htsmsg_add_u32(array, NULL, eq.result[i]->id);
}
htsmsg_add_msg(out, full ? "events" : "eventIds", array);
}
- epg_query_free(&eqr);
+ epg_query_free(&eq);
return out;
}
}
-/**
- *
- */
-static int
-extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- htsmsg_t *out, *array, *m;
- epg_query_result_t eqr;
- epg_broadcast_t *e;
- epg_episode_t *ee = NULL;
- epg_genre_t *eg = NULL, genre;
- channel_t *ch;
- int start = 0, end, limit, i;
- const char *s;
- char buf[100];
- const char *channel = http_arg_get(&hc->hc_req_args, "channel");
- const char *tag = http_arg_get(&hc->hc_req_args, "tag");
- const char *title = http_arg_get(&hc->hc_req_args, "title");
- const char *lang = http_arg_get(&hc->hc_args, "Accept-Language");
-
- int min_duration;
- int max_duration;
-
- if(channel && !channel[0]) channel = NULL;
- if(tag && !tag[0]) tag = NULL;
-
- if((s = http_arg_get(&hc->hc_req_args, "minduration")) != NULL)
- min_duration = atoi(s);
- else
- min_duration = 0;
-
- if((s = http_arg_get(&hc->hc_req_args, "maxduration")) != NULL)
- max_duration = atoi(s);
- else
- max_duration = INT_MAX;
-
- if((s = http_arg_get(&hc->hc_req_args, "start")) != NULL)
- start = atoi(s);
-
- if((s = http_arg_get(&hc->hc_req_args, "limit")) != NULL)
- limit = atoi(s);
- else
- limit = 20; /* XXX */
-
- if ((s = http_arg_get(&hc->hc_req_args, "content_type"))) {
- genre.code = atoi(s) * 16;
- eg = &genre;
- }
-
- out = htsmsg_create_map();
- array = htsmsg_create_list();
-
- pthread_mutex_lock(&global_lock);
-
- epg_query(&eqr, channel, tag, eg, title, lang, min_duration, max_duration);
-
- epg_query_sort(&eqr);
-
- htsmsg_add_u32(out, "totalCount", eqr.eqr_entries);
-
-
- start = MIN(start, eqr.eqr_entries);
- end = MIN(start + limit, eqr.eqr_entries);
-
- for(i = start; i < end; i++) {
- e = eqr.eqr_array[i];
- ee = e->episode;
- ch = e->channel;
- if (!ch||!ee) continue;
-
- m = htsmsg_create_map();
-
- htsmsg_add_str(m, "channel", channel_get_name(ch));
- htsmsg_add_u32(m, "channelid", channel_get_id(ch));
- if(ch->ch_icon != NULL)
- htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
-
- if((s = epg_episode_get_title(ee, lang)))
- htsmsg_add_str(m, "title", s);
- if((s = epg_episode_get_subtitle(ee, lang)))
- htsmsg_add_str(m, "subtitle", s);
-
- if((s = epg_broadcast_get_description(e, lang)))
- htsmsg_add_str(m, "description", s);
- else if((s = epg_broadcast_get_summary(e, lang)))
- htsmsg_add_str(m, "description", s);
-
- if (epg_episode_number_format(ee, buf, 100, NULL, "Season %d", ".",
- "Episode %d", "/%d"))
- htsmsg_add_str(m, "episode", buf);
-
- htsmsg_add_u32(m, "id", e->id);
- htsmsg_add_u32(m, "start", e->start);
- htsmsg_add_u32(m, "end", e->stop);
- htsmsg_add_u32(m, "duration", e->stop - e->start);
- if(ee->star_rating)
- htsmsg_add_u32(m, "starrating", ee->star_rating);
- if(ee->age_rating)
- htsmsg_add_u32(m, "agerating", ee->age_rating);
-
- if(e->serieslink)
- htsmsg_add_str(m, "serieslink", e->serieslink->uri);
-
- if((eg = LIST_FIRST(&ee->genre))) {
- htsmsg_add_u32(m, "content_type", eg->code / 16);
- }
-
- dvr_entry_t *de;
- if((de = dvr_entry_find_by_event(e)) != NULL)
- htsmsg_add_str(m, "schedstate", dvr_entry_schedstatus(de));
-
- htsmsg_add_msg(array, NULL, m);
- }
-
- epg_query_free(&eqr);
-
- 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;
-}
-
-static int
-extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- htsmsg_t *out, *array, *m;
- epg_broadcast_t *e, *ebc;
- epg_episode_t *ee, *ee2;
- channel_t *ch;
- uint32_t count = 0;
- const char *s;
- char buf[100];
-
- const char *lang = http_arg_get(&hc->hc_args, "Accept-Language");
- const char *id = http_arg_get(&hc->hc_req_args, "id");
- const char *type = http_arg_get(&hc->hc_req_args, "type");
-
- out = htsmsg_create_map();
- array = htsmsg_create_list();
-
- pthread_mutex_lock(&global_lock);
- if ( id && type ) {
- e = epg_broadcast_find_by_id(atoi(id), NULL);
- if ( e && e->episode ) {
- ee = e->episode;
-
- /* Alternative broadcasts */
- if (!strcmp(type, "alternative")) {
- LIST_FOREACH(ebc, &ee->broadcasts, ep_link) {
- ch = ebc->channel;
- if ( !ch ) continue; // skip something not viewable
- if ( ebc == e ) continue; // skip self
- count++;
- m = htsmsg_create_map();
- htsmsg_add_u32(m, "id", ebc->id);
- htsmsg_add_str(m, "channel", channel_get_name(ch));
- if (ch->ch_icon)
- htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
- htsmsg_add_u32(m, "start", ebc->start);
- htsmsg_add_msg(array, NULL, m);
- }
-
- /* Related */
- } else if (!strcmp(type, "related")) {
- if (ee->brand) {
- LIST_FOREACH(ee2, &ee->brand->episodes, blink) {
- if (ee2 == ee) continue;
- if (!ee2->title) continue;
- count++;
- m = htsmsg_create_map();
- htsmsg_add_str(m, "uri", ee2->uri);
- if ((s = epg_episode_get_title(ee2, lang)))
- htsmsg_add_str(m, "title", s);
- if ((s = epg_episode_get_subtitle(ee2, lang)))
- htsmsg_add_str(m, "subtitle", s);
- if (epg_episode_number_format(ee2, buf, 100, NULL, "Season %d",
- ".", "Episode %d", "/%d"))
- htsmsg_add_str(m, "episode", buf);
- htsmsg_add_msg(array, NULL, m);
- }
- } else if (ee->season) {
- LIST_FOREACH(ee2, &ee->season->episodes, slink) {
- if (ee2 == ee) continue;
- if (!ee2->title) continue;
- count++;
- m = htsmsg_create_map();
- htsmsg_add_str(m, "uri", ee2->uri);
- if ((s = epg_episode_get_title(ee2, lang)))
- htsmsg_add_str(m, "title", s);
- if ((s = epg_episode_get_subtitle(ee2, lang)))
- htsmsg_add_str(m, "subtitle", s);
- if (epg_episode_number_format(ee2, buf, 100, NULL, "Season %d",
- ".", "Episode %d", "/%d"))
- htsmsg_add_str(m, "episode", buf);
- htsmsg_add_msg(array, NULL, m);
- }
- }
- }
- }
- }
- pthread_mutex_unlock(&global_lock);
-
- htsmsg_add_u32(out, "totalCount", count);
- 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;
-}
-
-/**
- *
- */
-static int
-extjs_epgobject(http_connection_t *hc, const char *remain, void *opaque)
-{
- htsbuf_queue_t *hq = &hc->hc_reply;
- const char *op = http_arg_get(&hc->hc_req_args, "op");
- htsmsg_t *out, *array;
-
- if(op == NULL)
- return 400;
-
- if (!strcmp(op, "brandList")) {
- out = htsmsg_create_map();
- pthread_mutex_lock(&global_lock);
- array = epg_brand_list();
- pthread_mutex_unlock(&global_lock);
- htsmsg_add_msg(out, "entries", array);
-
- } else {
- return HTTP_STATUS_BAD_REQUEST;
- }
-
- htsmsg_json_serialize(out, hq, 0);
- htsmsg_destroy(out);
- http_output_content(hc, "text/x-json; charset=UTF-8");
-
- return 0;
-}
-
/**
*
*/
http_path_add("/capabilities", NULL, extjs_capabilities, ACCESS_WEB_INTERFACE);
http_path_add("/tablemgr", NULL, extjs_tablemgr, ACCESS_WEB_INTERFACE);
http_path_add("/epggrab", NULL, extjs_epggrab, ACCESS_WEB_INTERFACE);
- http_path_add("/epg", NULL, extjs_epg, ACCESS_WEB_INTERFACE);
- http_path_add("/epgrelated", NULL, extjs_epgrelated, ACCESS_WEB_INTERFACE);
- http_path_add("/epgobject", NULL, extjs_epgobject, ACCESS_WEB_INTERFACE);
http_path_add("/config", NULL, extjs_config, ACCESS_WEB_INTERFACE);
http_path_add("/languages", NULL, extjs_languages, ACCESS_WEB_INTERFACE);
#if ENABLE_TIMESHIFT
dvr_entry_t *de;
dvr_query_result_t dqr;
const char *rstatus = NULL;
- epg_query_result_t eqr;
const char *lang = http_arg_get(&hc->hc_args, "Accept-Language");
htsbuf_qprintf(hq, "<html>");
if(s != NULL) {
+ epg_query_t eq;
+
+ memset(&eq, 0, sizeof(eq));
+ eq.lang = strdup(lang);
//Note: force min/max durations for this interface to 0 and INT_MAX seconds respectively
- epg_query(&eqr, NULL, NULL, NULL, s, lang, 0, INT_MAX);
- epg_query_sort(&eqr);
+ epg_query(&eq);
- c = eqr.eqr_entries;
+ c = eq.entries;
- if(eqr.eqr_entries == 0) {
+ if(eq.entries == 0) {
htsbuf_qprintf(hq, "<b>No matching entries found</b>");
} else {
memset(&day, -1, sizeof(struct tm));
for(k = 0; k < c; k++) {
- e = eqr.eqr_array[k];
+ e = eq.result[k];
localtime_r(&e->start, &a);
localtime_r(&e->stop, &b);
}
}
htsbuf_qprintf(hq, "<hr>");
- epg_query_free(&eqr);
+ epg_query_free(&eq);
}
-tvheadend.brands = new Ext.data.JsonStore({
- root: 'entries',
- fields: ['uri', 'title'],
- autoLoad: true,
- url: 'epgobject',
- baseParams: {
- op: 'brandList'
- }
-});
-
insertContentGroupClearOption = function( scope, records, options ){
var placeholder = Ext.data.Record.create(['val', 'key']);
scope.insert(0,new placeholder({val: '(Clear filter)', key: '-1'}));
tvheadend.contentGroupLookupName = function(code) {
ret = "";
+ if (!code)
+ code = 0;
tvheadend.ContentGroupStore.each(function(r) {
+ if (r.data.key === code & 0xf0)
+ ret = r.data.val;
+ });
+ return ret;
+};
+
+tvheadend.ContentGroupFullStore = tvheadend.idnode_get_enum({
+ url: 'api/epg/content_type/list',
+ params: { full: 1 }
+});
+
+tvheadend.contentGroupFullLookupName = function(code) {
+ ret = "";
+ tvheadend.ContentGroupFullStore.each(function(r) {
if (r.data.key === code)
ret = r.data.val;
});
return channelString;
};
-tvheadend.tagLookupName = function(key) {
+tvheadend.channelTagLookupName = function(key) {
tagString = "";
var index = tvheadend.channelTags.find('key', key);
var content = '';
- if (event.chicon != null && event.chicon.length > 0)
- content += '<img class="x-epg-chicon" src="' + event.chicon + '">';
+ if (event.channelIcon != null && event.channelIcon.length > 0)
+ content += '<img class="x-epg-chicon" src="' + event.channelIcon + '">';
content += '<div class="x-epg-title">' + event.title;
if (event.subtitle)
content += " : " + event.subtitle;
content += '</div>';
- content += '<div class="x-epg-desc">' + event.episode + '</div>';
- content += '<div class="x-epg-desc">' + event.description + '</div>';
- content += '<div class="x-epg-meta">' + event.starrating + '</div>';
- content += '<div class="x-epg-meta">' + event.agerating + '</div>';
- content += '<div class="x-epg-meta">' + tvheadend.contentGroupLookupName(event.content_type) + '</div>';
-
- if (event.ext_desc != null)
- content += '<div class="x-epg-meta">' + event.ext_desc + '</div>';
-
- if (event.ext_item != null)
- content += '<div class="x-epg-meta">' + event.ext_item + '</div>';
-
- if (event.ext_text != null)
- content += '<div class="x-epg-meta">' + event.ext_text + '</div>';
+ if (event.episodeOnscreen)
+ content += '<div class="x-epg-desc">' + event.episodeOnscreen + '</div>';
+ if (event.summary)
+ content += '<div class="x-epg-desc"><b>' + event.summary + '</b></div>';
+ if (event.description)
+ content += '<div class="x-epg-desc"><p>' + event.description + '</p></div>';
+ if (event.starRating)
+ content += '<div class="x-epg-meta">Star Rating: ' + event.starRating + '</div>';
+ if (event.ageRating)
+ content += '<div class="x-epg-meta">Age Rating: ' + event.ageRating + '</div>';
+ if (event.genre) {
+ var genre = [];
+ Ext.each(event.genre, function(g) {
+ var g1 = tvheadend.contentGroupLookupName(g);
+ var g2 = tvheadend.contentGroupFullLookupName(g);
+ if (g1 == g2)
+ g1 = '';
+ if (g1 || g2)
+ genre.push((g1 ? '[' + g1 + '] ' : '') + g2);
+ });
+ content += '<div class="x-epg-meta">Content Type: ' + genre.join(', ') + '</div>';
+ }
content += '<div class="x-epg-meta"><a target="_blank" href="http://akas.imdb.com/find?q=' + event.title + '">Search IMDB</a></div>';
content += '<div id="related"></div>';
content += '<div id="altbcast"></div>';
now = new Date();
- if (event.start < now && event.end > now) {
+ if (event.start < now && event.stop > now) {
var title = event.title;
- if (event.episode)
- title += ' / ' + event.episode;
- content += '<div class="x-epg-meta"><a href="play/stream/channelid/' + event.channelid +
+ if (event.episodeOnscreen)
+ title += ' / ' + event.episodeOnscreen;
+ content += '<div class="x-epg-meta"><a href="play/stream/channel/' + event.channelUuid +
'?title=' + encodeURIComponent(title) + '">Play</a></div>';
}
}));
buttons.push(new Ext.Button({
handler: recordSeries,
- text: event.serieslink ? "Record series" : "Autorec"
+ text: event.serieslinkId ? "Record series" : "Autorec"
}));
} else {
constrainHeader: true,
buttons: buttons,
buttonAlign: 'center',
+ autoScroll: true,
html: content
});
win.show();
Ext.Ajax.request({
url: url,
params: {
- event_id: event.id,
+ event_id: event.eventId,
config_uuid: confcombo.getValue()
},
success: function(response, options) {
width: 20,
dataIndex: 'actions',
actions: [{
- iconIndex: 'schedstate'
+ iconIndex: 'dvrState'
}]
});
var epgStore = new Ext.ux.grid.livegrid.Store({
autoLoad: true,
- url: 'epg',
+ url: 'api/epg/events/grid',
bufferSize: 300,
reader: new Ext.ux.grid.livegrid.JsonReader({
root: 'entries',
totalProperty: 'totalCount',
- id: 'id'
- }, [{
- name: 'id'
- }, {
- name: 'channel'
- }, {
- name: 'channelid'
- }, {
- name: 'title'
- }, {
- name: 'subtitle'
- }, {
- name: 'episode'
- }, {
- name: 'description'
- }, {
- name: 'chicon'
- }, {
+ id: 'eventId',
+ },
+ [
+ { name: 'eventId' },
+ { name: 'channelName' },
+ { name: 'channelUuid' },
+ { name: 'channelNumber' },
+ { name: 'channelIcon' },
+ { name: 'title' },
+ { name: 'subtitle' },
+ { name: 'summary' },
+ { name: 'description' },
+ { name: 'episodeOnscreen' },
+ {
name: 'start',
type: 'date',
dateFormat: 'U' /* unix time */
- }, {
- name: 'end',
+ },
+ {
+ name: 'stop',
type: 'date',
dateFormat: 'U' /* unix time */
- }, {
- name: 'duration'
- }, {
- name: 'starrating'
- }, {
- name: 'agerating'
- }, {
- name: 'content_type'
- }, {
- name: 'schedstate'
- }, {
- name: 'serieslink'
- }])
+ },
+ { name: 'starRating' },
+ { name: 'ageRating' },
+ { name: 'genre' },
+ { name: 'dvrState' },
+ { name: 'serieslinkId' },
+ ]),
});
function setMetaAttr(meta, record) {
function renderDate(value, meta, record, rowIndex, colIndex, store) {
setMetaAttr(meta, record);
- var dt = new Date(value);
- return dt.format('D, M d, H:i');
+ if (value) {
+ var dt = new Date(value);
+ return dt.format('D, M d, H:i');
+ }
+ return "";
}
function renderDuration(value, meta, record, rowIndex, colIndex, store) {
setMetaAttr(meta, record);
- value = Math.floor(value / 60);
+ value = record.data.stop - record.data.start;
+ if (!value || value < 0)
+ value = 0;
+
+ value = Math.floor(value / 60000);
if (value >= 60) {
var min = value % 60;
return '' + value;
}
- var epgCm = new Ext.grid.ColumnModel([actions,
- new Ext.ux.grid.ProgressColumn({
- width: 100,
- header: "Progress",
- dataIndex: 'progress',
- colored: false,
- ceiling: 100,
- tvh_renderer: function(value, meta, record, rowIndex, colIndex, store) {
- var entry = record.data;
- var start = entry.start;
- var end = entry.end;
- var duration = entry.duration; // seconds
- var now = new Date();
-
- // Only render a progress bar for currently running programmes
- if (now >= start)
- return (now - start) / 1000 / duration * 100;
- else
- return "";
- }
- }), {
- width: 250,
- id: 'title',
- header: "Title",
- dataIndex: 'title',
- renderer: renderText
- }, {
- width: 250,
- id: 'subtitle',
- header: "SubTitle",
- dataIndex: 'subtitle',
- renderer: renderText
- }, {
- width: 100,
- id: 'episode',
- header: "Episode",
- dataIndex: 'episode',
- renderer: renderText
- }, {
- width: 100,
- id: 'start',
- header: "Start",
- dataIndex: 'start',
- renderer: renderDate
- }, {
- width: 100,
- hidden: true,
- id: 'end',
- header: "End",
- dataIndex: 'end',
- renderer: renderDate
- }, {
- width: 100,
- id: 'duration',
- header: "Duration",
- dataIndex: 'duration',
- renderer: renderDuration
- }, {
- width: 250,
- id: 'channel',
- header: "Channel",
- dataIndex: 'channel',
- renderer: renderText
- }, {
- width: 50,
- id: 'starrating',
- header: "Stars",
- dataIndex: 'starrating',
- renderer: renderInt
- }, {
- width: 50,
- id: 'agerating',
- header: "Age",
- dataIndex: 'agerating',
- renderer: renderInt
- }, {
- width: 250,
- id: 'content_type',
- header: "Content Type",
- dataIndex: 'content_type',
- renderer: function(v) {
- return tvheadend.contentGroupLookupName(v);
+ var epgCm = new Ext.grid.ColumnModel({
+ defaultSortable: true,
+ columns: [
+ actions,
+ new Ext.ux.grid.ProgressColumn({
+ width: 100,
+ header: "Progress",
+ dataIndex: 'progress',
+ colored: false,
+ ceiling: 100,
+ tvh_renderer: function(value, meta, record, rowIndex, colIndex, store) {
+ var entry = record.data;
+ var start = entry.start; // milliseconds
+ var duration = entry.stop - start; // milliseconds
+ var now = new Date();
+
+ if (!duration || duration < 0) duration = 0;
+ // Only render a progress bar for currently running programmes
+ if (now >= start && now - start <= duration)
+ return (now - start) / duration * 100;
+ else
+ return "";
+ }
+ }),
+ {
+ width: 250,
+ id: 'title',
+ header: "Title",
+ dataIndex: 'title',
+ renderer: renderText
+ },
+ {
+ width: 250,
+ id: 'subtitle',
+ header: "SubTitle",
+ dataIndex: 'subtitle',
+ renderer: renderText
+ },
+ {
+ width: 100,
+ id: 'episodeOnscreen',
+ header: "Episode",
+ dataIndex: 'episodeOnscreen',
+ renderer: renderText
+ },
+ {
+ width: 100,
+ id: 'start',
+ header: "Start",
+ dataIndex: 'start',
+ renderer: renderDate
+ },
+ {
+ width: 100,
+ hidden: true,
+ id: 'stop',
+ header: "End",
+ dataIndex: 'stop',
+ renderer: renderDate
+ },
+ {
+ width: 100,
+ id: 'duration',
+ header: "Duration",
+ renderer: renderDuration
+ },
+ {
+ width: 60,
+ id: 'channelNumber',
+ header: "Number",
+ align: 'right',
+ dataIndex: 'channelNumber',
+ renderer: renderText
+ },
+ {
+ width: 250,
+ id: 'channelName',
+ header: "Channel",
+ dataIndex: 'channelName',
+ renderer: renderText
+ },
+ {
+ width: 50,
+ id: 'starRating',
+ header: "Stars",
+ dataIndex: 'starRating',
+ renderer: renderInt
+ },
+ {
+ width: 50,
+ id: 'ageRating',
+ header: "Age",
+ dataIndex: 'ageRating',
+ renderer: renderInt
+ }, {
+ width: 250,
+ id: 'genre',
+ header: "Content Type",
+ dataIndex: 'genre',
+ renderer: function(vals) {
+ var r = [];
+ Ext.each(vals, function(v) {
+ v = tvheadend.contentGroupFullLookupName(v);
+ if (v)
+ r.push(v);
+ });
+ return r.join(',');
+ }
}
- }]);
+ ]
+ });
+
+ var filter = new Ext.ux.grid.GridFilters({
+ encode: true,
+ local: false,
+ filters: [
+ { type: 'string', dataIndex: 'title' },
+ { type: 'string', dataIndex: 'subtitle' },
+ { type: 'string', dataIndex: 'episodeOnscreen' },
+ { type: 'intsplit', dataIndex: 'channelNumber', intsplit: 1000000 },
+ { type: 'string', dataIndex: 'channelName' },
+ { type: 'numeric', dataIndex: 'starRating' },
+ { type: 'numeric', dataIndex: 'ageRating' }
+ ]
+ });
// Title search box
blur: function () {
if(this.getRawValue() == "" ) {
clearChannelFilter();
- epgStore.reload();
+ epgView.reset();
}
}
}
blur: function () {
if(this.getRawValue() == "" ) {
clearChannelTagsFilter();
- epgStore.reload();
+ epgView.reset();
}
}
}
blur: function () {
if(this.getRawValue() == "" ) {
clearContentGroupFilter();
- epgStore.reload();
+ epgView.reset();
}
}
}
blur: function () {
if(this.getRawValue() == "" ) {
clearDurationFilter();
- epgStore.reload();
+ epgView.reset();
}
}
}
};
clearChannelTagsFilter = function() {
- delete epgStore.baseParams.tag;
+ delete epgStore.baseParams.channelTag;
epgFilterChannelTags.setValue("");
};
clearContentGroupFilter = function() {
- delete epgStore.baseParams.content_type;
+ delete epgStore.baseParams.contentType;
epgFilterContentGroup.setValue("");
};
clearDurationFilter = function() {
- delete epgStore.baseParams.minduration;
- delete epgStore.baseParams.maxduration;
+ delete epgStore.baseParams.durationMin;
+ delete epgStore.baseParams.durationMax;
epgFilterDuration.setValue("");
};
clearChannelTagsFilter();
clearDurationFilter();
clearContentGroupFilter();
- epgStore.reload();
+ filter.clearFilters();
+ delete epgStore.sortInfo;
+ epgView.reset();
};
/*
clearChannelFilter();
else if (epgStore.baseParams.channel !== r.data.key)
epgStore.baseParams.channel = r.data.key;
- epgStore.reload();
+ epgView.reset();
});
epgFilterChannelTags.on('select', function(c, r) {
if (r.data.key == -1)
clearChannelTagsFilter();
- else if (epgStore.baseParams.tag !== r.data.key)
- epgStore.baseParams.tag = r.data.key;
- epgStore.reload();
+ else if (epgStore.baseParams.channelTag !== r.data.key)
+ epgStore.baseParams.channelTag = r.data.key;
+ epgView.reset();
});
epgFilterContentGroup.on('select', function(c, r) {
if (r.data.key == -1)
clearContentGroupFilter();
- else if (epgStore.baseParams.content_type !== r.data.key)
- epgStore.baseParams.content_type = r.data.key;
- epgStore.reload();
+ else if (epgStore.baseParams.contentType !== r.data.key)
+ epgStore.baseParams.contentType = r.data.key;
+ epgView.reset();
});
epgFilterDuration.on('select', function(c, r) {
if (r.data.identifier == -1)
clearDurationFilter();
- else if (epgStore.baseParams.minduration !== r.data.minvalue) {
- epgStore.baseParams.minduration = r.data.minvalue;
- epgStore.baseParams.maxduration = r.data.maxvalue;
+ else if (epgStore.baseParams.durationMin !== r.data.minvalue) {
+ epgStore.baseParams.durationMin = r.data.minvalue;
+ epgStore.baseParams.durationMax = r.data.maxvalue;
}
- epgStore.reload();
+ epgView.reset();
});
epgFilterTitle.on('valid', function(c) {
if (epgStore.baseParams.title !== value) {
epgStore.baseParams.title = value;
- epgStore.reload();
+ epgView.reset();
}
});
nearLimit: 100,
loadMask: {
msg: 'Buffering. Please wait...'
+ },
+ listeners: {
+ beforebuffer: {
+ fn: function(view, ds, index, range, total, options) {
+ /* filters hack */
+ filter.onBeforeLoad(ds, options);
+ }
+ }
}
});
stateId: 'epggrid',
enableDragDrop: false,
cm: epgCm,
- plugins: [actions],
+ plugins: [filter, actions],
title: 'Electronic Program Guide',
iconCls: 'newspaper',
store: epgStore,
});
panel.on('rowclick', rowclicked);
-
+ panel.on('filterupdate', function() {
+ epgView.reset();
+ });
+
/**
* Listener for DVR notifications. We want to update the EPG grid when a
* recording is finished/deleted etc. so the status icon gets updated.
// Always reload the store when the tab is activated
panel.on('beforeshow', function() {
- this.store.reload();
+ epgStore.reload();
});
function rowclicked(grid, index) {
: "<i>Don't care</i>";
var channel = epgStore.baseParams.channel ? tvheadend.channelLookupName(epgStore.baseParams.channel)
: "<i>Don't care</i>";
- var tag = epgStore.baseParams.tag ? tvheadend.tagLookupName(epgStore.baseParams.tag)
+ var tag = epgStore.baseParams.channelTag ? tvheadend.channelTagLookupName(epgStore.baseParams.channelTag)
: "<i>Don't care</i>";
- var content_type = epgStore.baseParams.content_type ? tvheadend.contentGroupLookupName(epgStore.baseParams.content_type)
+ var contentType = epgStore.baseParams.contentType ? tvheadend.contentGroupLookupName(epgStore.baseParams.contentType)
: "<i>Don't care</i>";
- var duration = epgStore.baseParams.minduration ? tvheadend.durationLookupRange(epgStore.baseParams.minduration)
+ var duration = epgStore.baseParams.durationMin ? tvheadend.durationLookupRange(epgStore.baseParams.durationMin)
: "<i>Don't care</i>";
Ext.MessageBox.confirm('Auto Recorder', 'This will create an automatic rule that '
+ '<div class="x-smallhdr">Title:</div>' + title + '<br>'
+ '<div class="x-smallhdr">Channel:</div>' + channel + '<br>'
+ '<div class="x-smallhdr">Tag:</div>' + tag + '<br>'
- + '<div class="x-smallhdr">Genre:</div>' + content_type + '<br>'
+ + '<div class="x-smallhdr">Genre:</div>' + contentType + '<br>'
+ '<div class="x-smallhdr">Duration:</div>' + duration + '<br>'
+ '<br><br>' + 'Currently this will match (and record) '
+ epgStore.getTotalCount() + ' events. ' + 'Are you sure?',
if (params.title) conf.title = params.title;
if (params.channel) conf.channel = params.channel;
if (params.tag) conf.tag = params.tag;
- if (params.content_type) conf.content_type = params.content_type;
- if (params.minduration) conf.minduration = params.minduration;
- if (params.maxduration) conf.maxduration = params.maxduration;
+ if (params.contentType) conf.content_type = params.contentType;
+ if (params.durationMin) conf.minduration = params.durationMin;
+ if (params.durationMax) conf.maxduration = params.durationMax;
Ext.Ajax.request({
url: 'api/dvr/autorec/create',
params: { conf: Ext.encode(conf) }