epg: query API rewrite
This commit is contained in:
parent
ba136c5392
commit
0cb841a427
10 changed files with 1143 additions and 592 deletions
|
@ -25,7 +25,7 @@
|
|||
#include "redblack.h"
|
||||
#include "access.h"
|
||||
|
||||
#define TVH_API_VERSION 14
|
||||
#define TVH_API_VERSION 15
|
||||
|
||||
/*
|
||||
* Command hook
|
||||
|
|
|
@ -22,8 +22,52 @@
|
|||
#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 )
|
||||
{
|
||||
|
@ -31,8 +75,9 @@ 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;
|
||||
|
@ -40,8 +85,6 @@ api_epg_entry ( epg_broadcast_t *eb, const char *lang )
|
|||
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);
|
||||
|
@ -55,25 +98,17 @@ api_epg_entry ( epg_broadcast_t *eb, const char *lang )
|
|||
}
|
||||
|
||||
/* 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)))
|
||||
|
@ -108,11 +143,29 @@ api_epg_entry ( epg_broadcast_t *eb, const char *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)))
|
||||
|
@ -121,30 +174,245 @@ api_epg_entry ( epg_broadcast_t *eb, const char *lang )
|
|||
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));
|
||||
|
||||
min_duration = htsmsg_get_u32_or_default(args, "minduration", 0);
|
||||
max_duration = htsmsg_get_u32_or_default(args, "maxduration", INT_MAX);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
@ -152,27 +420,124 @@ api_epg_grid
|
|||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -181,19 +546,24 @@ api_epg_content_type_list(access_t *perm, void *opaque, const char *op,
|
|||
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 },
|
||||
};
|
||||
|
|
|
@ -101,7 +101,7 @@ api_idnode_grid_conf
|
|||
/* 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;
|
||||
|
|
|
@ -386,6 +386,8 @@ channel_t *
|
|||
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;
|
||||
|
@ -931,6 +933,9 @@ channel_tag_find_by_name(const char *name, int create)
|
|||
{
|
||||
channel_tag_t *ct;
|
||||
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
TAILQ_FOREACH(ct, &channel_tags, ct_link)
|
||||
if(!strcasecmp(ct->ct_name, name))
|
||||
return ct;
|
||||
|
|
436
src/epg.c
436
src/epg.c
|
@ -1752,6 +1752,12 @@ const char *epg_broadcast_get_title ( epg_broadcast_t *b, const char *lang )
|
|||
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;
|
||||
|
@ -2190,7 +2196,7 @@ htsmsg_t *epg_genres_list_all ( int major_only, int major_prefix )
|
|||
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);
|
||||
|
@ -2204,109 +2210,393 @@ htsmsg_t *epg_genres_list_all ( int major_only, int major_prefix )
|
|||
* 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 )
|
||||
static inline int
|
||||
_eq_comp_num ( epg_filter_num_t *f, int64_t val )
|
||||
{
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
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;
|
||||
}
|
||||
|
||||
/* Store */
|
||||
eqr->eqr_array[eqr->eqr_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 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 (eq->entries == eq->allocated) {
|
||||
eq->allocated = MAX(100, eq->allocated + 100);
|
||||
eq->result = realloc(eq->result, eq->allocated * sizeof(epg_broadcast_t *));
|
||||
}
|
||||
|
||||
/* Store */
|
||||
eq->result[eq->entries++] = e;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 ( title ) {
|
||||
if (regcomp(&preg0, title, REG_ICASE | REG_EXTENDED | REG_NOSUB) )
|
||||
return;
|
||||
preg = &preg0;
|
||||
} else {
|
||||
preg = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (preg) regfree(preg);
|
||||
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
tvh_qsort_r(eq->result, eq->entries, sizeof(epg_broadcast_t *), fcn, eq);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
return eq->result;
|
||||
}
|
||||
|
||||
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)
|
||||
void epg_query_free(epg_query_t *eq)
|
||||
{
|
||||
channel_t *ch = channel ? channel_find(channel) : NULL;
|
||||
channel_tag_t *ct = tag ? channel_tag_find_by_uuid(tag) : NULL;
|
||||
|
||||
epg_query0(eqr, ch, ct, genre, title, lang, min_duration, max_duration);
|
||||
free(eq->result); eq->result = NULL;
|
||||
}
|
||||
|
||||
void epg_query_free(epg_query_result_t *eqr)
|
||||
{
|
||||
free(eqr->eqr_array);
|
||||
}
|
||||
|
||||
static int _epg_sort_start_ascending ( const void *a, const void *b )
|
||||
{
|
||||
return (*(epg_broadcast_t**)a)->start - (*(epg_broadcast_t**)b)->start;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
91
src/epg.h
91
src/epg.h
|
@ -19,6 +19,7 @@
|
|||
#ifndef EPG_H
|
||||
#define EPG_H
|
||||
|
||||
#include <regex.h>
|
||||
#include "settings.h"
|
||||
#include "lang_str.h"
|
||||
|
||||
|
@ -507,6 +508,8 @@ epg_episode_t *epg_broadcast_get_episode
|
|||
( 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
|
||||
|
@ -528,28 +531,80 @@ void epg_channel_unlink ( struct channel *ch );
|
|||
* Querying
|
||||
* ***********************************************************************/
|
||||
|
||||
/*
|
||||
* Query result
|
||||
*/
|
||||
typedef struct epg_query_result {
|
||||
epg_broadcast_t **eqr_array;
|
||||
int eqr_entries;
|
||||
int eqr_alloced;
|
||||
} epg_query_result_t;
|
||||
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;
|
||||
|
||||
void epg_query_free(epg_query_result_t *eqr);
|
||||
typedef struct epg_filter_str {
|
||||
char *str;
|
||||
regex_t re;
|
||||
epg_comp_t comp;
|
||||
} epg_filter_str_t;
|
||||
|
||||
/* Sorting */
|
||||
// WIBNI: might be useful to have a user defined comparator function
|
||||
void epg_query_sort(epg_query_result_t *eqr);
|
||||
typedef struct epg_filter_num {
|
||||
int64_t val1;
|
||||
int64_t val2;
|
||||
epg_comp_t comp;
|
||||
} epg_filter_num_t;
|
||||
|
||||
/* 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 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
|
||||
|
|
|
@ -1142,8 +1142,7 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
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;
|
||||
|
@ -1151,48 +1150,61 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
/* 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;
|
||||
}
|
||||
|
|
|
@ -540,253 +540,6 @@ extjs_languages(http_connection_t *hc, const char *remain, void *opaque)
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -1062,9 +815,6 @@ extjs_start(void)
|
|||
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
|
||||
|
|
|
@ -164,7 +164,6 @@ page_simple(http_connection_t *hc,
|
|||
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>");
|
||||
|
@ -184,14 +183,17 @@ page_simple(http_connection_t *hc,
|
|||
|
||||
|
||||
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 {
|
||||
|
||||
|
@ -206,7 +208,7 @@ page_simple(http_connection_t *hc,
|
|||
|
||||
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);
|
||||
|
@ -234,7 +236,7 @@ page_simple(http_connection_t *hc,
|
|||
}
|
||||
}
|
||||
htsbuf_qprintf(hq, "<hr>");
|
||||
epg_query_free(&eqr);
|
||||
epg_query_free(&eq);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,13 +1,3 @@
|
|||
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'}));
|
||||
|
@ -22,7 +12,23 @@ tvheadend.ContentGroupStore = tvheadend.idnode_get_enum({
|
|||
|
||||
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;
|
||||
});
|
||||
|
@ -40,7 +46,7 @@ tvheadend.channelLookupName = function(key) {
|
|||
return channelString;
|
||||
};
|
||||
|
||||
tvheadend.tagLookupName = function(key) {
|
||||
tvheadend.channelTagLookupName = function(key) {
|
||||
tagString = "";
|
||||
|
||||
var index = tvheadend.channelTags.find('key', key);
|
||||
|
@ -83,38 +89,46 @@ tvheadend.epgDetails = function(event) {
|
|||
|
||||
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>';
|
||||
}
|
||||
|
||||
|
@ -158,7 +172,7 @@ tvheadend.epgDetails = function(event) {
|
|||
}));
|
||||
buttons.push(new Ext.Button({
|
||||
handler: recordSeries,
|
||||
text: event.serieslink ? "Record series" : "Autorec"
|
||||
text: event.serieslinkId ? "Record series" : "Autorec"
|
||||
}));
|
||||
|
||||
} else {
|
||||
|
@ -177,6 +191,7 @@ tvheadend.epgDetails = function(event) {
|
|||
constrainHeader: true,
|
||||
buttons: buttons,
|
||||
buttonAlign: 'center',
|
||||
autoScroll: true,
|
||||
html: content
|
||||
});
|
||||
win.show();
|
||||
|
@ -193,7 +208,7 @@ tvheadend.epgDetails = function(event) {
|
|||
Ext.Ajax.request({
|
||||
url: url,
|
||||
params: {
|
||||
event_id: event.id,
|
||||
event_id: event.eventId,
|
||||
config_uuid: confcombo.getValue()
|
||||
},
|
||||
success: function(response, options) {
|
||||
|
@ -212,55 +227,46 @@ tvheadend.epg = function() {
|
|||
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) {
|
||||
|
@ -275,14 +281,21 @@ tvheadend.epg = function() {
|
|||
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;
|
||||
|
@ -310,90 +323,131 @@ tvheadend.epg = function() {
|
|||
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();
|
||||
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();
|
||||
|
||||
// Only render a progress bar for currently running programmes
|
||||
if (now >= start)
|
||||
return (now - start) / 1000 / duration * 100;
|
||||
else
|
||||
return "";
|
||||
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(',');
|
||||
}
|
||||
}
|
||||
}), {
|
||||
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 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
|
||||
|
||||
|
@ -419,7 +473,7 @@ tvheadend.epg = function() {
|
|||
blur: function () {
|
||||
if(this.getRawValue() == "" ) {
|
||||
clearChannelFilter();
|
||||
epgStore.reload();
|
||||
epgView.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -441,7 +495,7 @@ tvheadend.epg = function() {
|
|||
blur: function () {
|
||||
if(this.getRawValue() == "" ) {
|
||||
clearChannelTagsFilter();
|
||||
epgStore.reload();
|
||||
epgView.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -465,7 +519,7 @@ tvheadend.epg = function() {
|
|||
blur: function () {
|
||||
if(this.getRawValue() == "" ) {
|
||||
clearContentGroupFilter();
|
||||
epgStore.reload();
|
||||
epgView.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -486,7 +540,7 @@ tvheadend.epg = function() {
|
|||
blur: function () {
|
||||
if(this.getRawValue() == "" ) {
|
||||
clearDurationFilter();
|
||||
epgStore.reload();
|
||||
epgView.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -508,18 +562,18 @@ tvheadend.epg = function() {
|
|||
};
|
||||
|
||||
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("");
|
||||
};
|
||||
|
||||
|
@ -529,7 +583,9 @@ tvheadend.epg = function() {
|
|||
clearChannelTagsFilter();
|
||||
clearDurationFilter();
|
||||
clearContentGroupFilter();
|
||||
epgStore.reload();
|
||||
filter.clearFilters();
|
||||
delete epgStore.sortInfo;
|
||||
epgView.reset();
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -541,33 +597,33 @@ tvheadend.epg = function() {
|
|||
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) {
|
||||
|
@ -578,7 +634,7 @@ tvheadend.epg = function() {
|
|||
|
||||
if (epgStore.baseParams.title !== value) {
|
||||
epgStore.baseParams.title = value;
|
||||
epgStore.reload();
|
||||
epgView.reset();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -586,6 +642,14 @@ tvheadend.epg = function() {
|
|||
nearLimit: 100,
|
||||
loadMask: {
|
||||
msg: 'Buffering. Please wait...'
|
||||
},
|
||||
listeners: {
|
||||
beforebuffer: {
|
||||
fn: function(view, ds, index, range, total, options) {
|
||||
/* filters hack */
|
||||
filter.onBeforeLoad(ds, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -632,7 +696,7 @@ tvheadend.epg = function() {
|
|||
stateId: 'epggrid',
|
||||
enableDragDrop: false,
|
||||
cm: epgCm,
|
||||
plugins: [actions],
|
||||
plugins: [filter, actions],
|
||||
title: 'Electronic Program Guide',
|
||||
iconCls: 'newspaper',
|
||||
store: epgStore,
|
||||
|
@ -646,7 +710,10 @@ tvheadend.epg = function() {
|
|||
});
|
||||
|
||||
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.
|
||||
|
@ -660,7 +727,7 @@ tvheadend.epg = function() {
|
|||
|
||||
// Always reload the store when the tab is activated
|
||||
panel.on('beforeshow', function() {
|
||||
this.store.reload();
|
||||
epgStore.reload();
|
||||
});
|
||||
|
||||
function rowclicked(grid, index) {
|
||||
|
@ -676,11 +743,11 @@ tvheadend.epg = function() {
|
|||
: "<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 '
|
||||
|
@ -689,7 +756,7 @@ tvheadend.epg = function() {
|
|||
+ '<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?',
|
||||
|
@ -709,9 +776,9 @@ tvheadend.epg = function() {
|
|||
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) }
|
||||
|
|
Loading…
Add table
Reference in a new issue