Merge branch 'epg-rewrite' into initialscan
This commit is contained in:
commit
140796a889
18 changed files with 406 additions and 362 deletions
|
@ -164,6 +164,8 @@ channel_create(const char *name, int number)
|
|||
channel_t *ch, *x;
|
||||
int id;
|
||||
|
||||
if (!name || !*name) return NULL;
|
||||
|
||||
ch = RB_LAST(&channel_identifier_tree);
|
||||
if(ch == NULL) {
|
||||
id = 1;
|
||||
|
@ -330,6 +332,8 @@ channel_rename(channel_t *ch, const char *newname)
|
|||
|
||||
lock_assert(&global_lock);
|
||||
|
||||
if (!newname || !*newname) return 0;
|
||||
|
||||
if(channel_find_by_name(newname, 0, 0))
|
||||
return -1;
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ typedef struct dvr_entry {
|
|||
char *de_ititle; /* Internal title optionally with channelname
|
||||
date and time pre/post/fixed */
|
||||
char *de_desc; /* Description in UTF-8 (from EPG) */
|
||||
uint8_t de_content_type; /* Content type (from EPG) */
|
||||
epg_genre_t de_content_type; /* Content type (from EPG) */
|
||||
|
||||
dvr_prio_t de_pri;
|
||||
|
||||
|
@ -205,7 +205,7 @@ typedef struct dvr_autorec_entry {
|
|||
char *dae_title;
|
||||
regex_t dae_title_preg;
|
||||
|
||||
uint8_t dae_content_type;
|
||||
epg_genre_t dae_content_type;
|
||||
|
||||
int dae_approx_time; /* Minutes from midnight */
|
||||
|
||||
|
@ -259,7 +259,7 @@ dvr_entry_t *dvr_entry_create(const char *dvr_config_name,
|
|||
channel_t *ch, time_t start, time_t stop,
|
||||
time_t start_extra, time_t stop_extra,
|
||||
const char *title, const char *description,
|
||||
uint8_t content_type,
|
||||
epg_genre_t *content_type,
|
||||
const char *creator, dvr_autorec_entry_t *dae,
|
||||
dvr_prio_t pri);
|
||||
|
||||
|
@ -327,7 +327,7 @@ void dvr_query_sort(dvr_query_result_t *dqr);
|
|||
*/
|
||||
void dvr_autorec_add(const char *dvr_config_name,
|
||||
const char *title, const char *channel,
|
||||
const char *tag, uint8_t content_type,
|
||||
const char *tag, epg_genre_t *content_type,
|
||||
const char *creator, const char *comment);
|
||||
|
||||
void dvr_autorec_add_series_link(const char *dvr_config_name,
|
||||
|
|
|
@ -74,7 +74,7 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
|
|||
|
||||
if(dae->dae_channel == NULL &&
|
||||
dae->dae_channel_tag == NULL &&
|
||||
dae->dae_content_type == 0 &&
|
||||
dae->dae_content_type.code == 0 &&
|
||||
(dae->dae_title == NULL ||
|
||||
dae->dae_title[0] == '\0') &&
|
||||
dae->dae_brand == NULL &&
|
||||
|
@ -91,7 +91,7 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
|
|||
if(dae->dae_title != NULL && dae->dae_title[0] != '\0') {
|
||||
if(e->episode->title == NULL ||
|
||||
regexec(&dae->dae_title_preg, e->episode->title, 0, NULL, 0))
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Note: ignore channel test if we allow quality unlocking
|
||||
|
@ -109,15 +109,9 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(dae->dae_content_type != 0) {
|
||||
int i, ok = 0;
|
||||
for (i = 0; i < e->episode->genre_cnt; i++) {
|
||||
if (e->episode->genre[i] == dae->dae_content_type) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) return 0;
|
||||
if(dae->dae_content_type.code != 0) {
|
||||
if (!epg_genre_list_contains(&e->episode->genre, &dae->dae_content_type, 1))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(dae->dae_approx_time != 0) {
|
||||
|
@ -275,7 +269,7 @@ autorec_record_build(dvr_autorec_entry_t *dae)
|
|||
if(dae->dae_channel_tag != NULL)
|
||||
htsmsg_add_str(e, "tag", dae->dae_channel_tag->ct_name);
|
||||
|
||||
htsmsg_add_u32(e, "contenttype",dae->dae_content_type);
|
||||
htsmsg_add_u32(e, "contenttype",dae->dae_content_type.code);
|
||||
|
||||
htsmsg_add_str(e, "title", dae->dae_title ?: "");
|
||||
|
||||
|
@ -389,7 +383,7 @@ autorec_record_update(void *opaque, const char *id, htsmsg_t *values,
|
|||
}
|
||||
}
|
||||
|
||||
dae->dae_content_type = htsmsg_get_u32_or_default(values, "contenttype", 0);
|
||||
dae->dae_content_type.code = htsmsg_get_u32_or_default(values, "contenttype", 0);
|
||||
|
||||
if((s = htsmsg_get_str(values, "approx_time")) != NULL) {
|
||||
if(strchr(s, ':') != NULL) {
|
||||
|
@ -470,7 +464,7 @@ dvr_autorec_init(void)
|
|||
static void
|
||||
_dvr_autorec_add(const char *config_name,
|
||||
const char *title, channel_t *ch,
|
||||
const char *tag, uint8_t content_type,
|
||||
const char *tag, epg_genre_t *content_type,
|
||||
epg_brand_t *brand, epg_season_t *season,
|
||||
int approx_time, epg_episode_num_t *epnum,
|
||||
const char *creator, const char *comment)
|
||||
|
@ -503,7 +497,8 @@ _dvr_autorec_add(const char *config_name,
|
|||
}
|
||||
|
||||
dae->dae_enabled = 1;
|
||||
dae->dae_content_type = content_type;
|
||||
if (content_type)
|
||||
dae->dae_content_type.code = content_type->code;
|
||||
|
||||
if(brand) {
|
||||
dae->dae_brand = brand;
|
||||
|
@ -535,7 +530,7 @@ _dvr_autorec_add(const char *config_name,
|
|||
void
|
||||
dvr_autorec_add(const char *config_name,
|
||||
const char *title, const char *channel,
|
||||
const char *tag, uint8_t content_type,
|
||||
const char *tag, epg_genre_t *content_type,
|
||||
const char *creator, const char *comment)
|
||||
{
|
||||
channel_t *ch = NULL;
|
||||
|
|
|
@ -249,7 +249,7 @@ static dvr_entry_t *_dvr_entry_create (
|
|||
channel_t *ch, time_t start, time_t stop,
|
||||
time_t start_extra, time_t stop_extra,
|
||||
const char *title, const char *description,
|
||||
uint8_t content_type,
|
||||
epg_genre_t *content_type,
|
||||
const char *creator, dvr_autorec_entry_t *dae,
|
||||
dvr_prio_t pri)
|
||||
{
|
||||
|
@ -301,7 +301,7 @@ static dvr_entry_t *_dvr_entry_create (
|
|||
de->de_creator = strdup(creator);
|
||||
de->de_title = strdup(title);
|
||||
de->de_desc = description ? strdup(description) : NULL;
|
||||
de->de_content_type = content_type;
|
||||
if (content_type) de->de_content_type = *content_type;
|
||||
de->de_bcast = e;
|
||||
if (e) e->getref((epg_object_t*)e);
|
||||
|
||||
|
@ -334,7 +334,7 @@ dvr_entry_create(const char *config_name,
|
|||
channel_t *ch, time_t start, time_t stop,
|
||||
time_t start_extra, time_t stop_extra,
|
||||
const char *title, const char *description,
|
||||
uint8_t content_type,
|
||||
epg_genre_t *content_type,
|
||||
const char *creator, dvr_autorec_entry_t *dae,
|
||||
dvr_prio_t pri)
|
||||
{
|
||||
|
@ -363,7 +363,7 @@ dvr_entry_create_by_event(const char *config_name,
|
|||
e->episode->title,
|
||||
e->episode->description ? e->episode->description
|
||||
: e->episode->summary,
|
||||
e->episode->genre_cnt ? e->episode->genre[0] : 0,
|
||||
LIST_FIRST(&e->episode->genre),
|
||||
creator, dae, pri);
|
||||
}
|
||||
|
||||
|
@ -530,7 +530,7 @@ dvr_db_load_one(htsmsg_t *c, int id)
|
|||
}
|
||||
|
||||
|
||||
de->de_content_type = htsmsg_get_u32_or_default(c, "contenttype", 0);
|
||||
de->de_content_type.code = htsmsg_get_u32_or_default(c, "contenttype", 0);
|
||||
|
||||
if (!htsmsg_get_u32(c, "broadcast", &bcid)) {
|
||||
de->de_bcast = epg_broadcast_find_by_id(bcid, ch);
|
||||
|
@ -606,8 +606,8 @@ dvr_entry_save(dvr_entry_t *de)
|
|||
if(de->de_autorec != NULL)
|
||||
htsmsg_add_str(m, "autorec", de->de_autorec->dae_id);
|
||||
|
||||
if(de->de_content_type)
|
||||
htsmsg_add_u32(m, "contenttype", de->de_content_type);
|
||||
if(de->de_content_type.code)
|
||||
htsmsg_add_u32(m, "contenttype", de->de_content_type.code);
|
||||
|
||||
if(de->de_bcast)
|
||||
htsmsg_add_u32(m, "broadcast", de->de_bcast->id);
|
||||
|
@ -666,10 +666,12 @@ static dvr_entry_t *_dvr_entry_update
|
|||
}
|
||||
|
||||
if (e) {
|
||||
if (e->episode &&
|
||||
e->episode->genre_cnt && e->episode->genre_cnt != de->de_content_type) {
|
||||
de->de_content_type = e->episode->genre[0];
|
||||
save = 1;
|
||||
epg_genre_t *g;
|
||||
if (e->episode && (g = LIST_FIRST(&e->episode->genre))) {
|
||||
if (g->code != de->de_content_type.code) {
|
||||
de->de_content_type.code = g->code;
|
||||
save = 1;
|
||||
}
|
||||
}
|
||||
if (de->de_bcast != e) {
|
||||
de->de_bcast->putref((epg_object_t*)de->de_bcast);
|
||||
|
|
|
@ -464,9 +464,9 @@ static htsbuf_queue_t *
|
|||
_mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
||||
{
|
||||
htsbuf_queue_t *q = htsbuf_queue_alloc(0);
|
||||
char datestr[64];
|
||||
char datestr[64], ctype[100];
|
||||
const epg_genre_t *eg = NULL;
|
||||
struct tm tm;
|
||||
const char *ctype = NULL;
|
||||
localtime_r(de ? &de->de_start : &ebc->start, &tm);
|
||||
epg_episode_t *ee = NULL;
|
||||
channel_t *ch;
|
||||
|
@ -490,12 +490,12 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
|
||||
addtag(q, build_tag_string("ORIGINAL_MEDIA_TYPE", "TV", 0, NULL));
|
||||
|
||||
if(de && de->de_content_type) {
|
||||
ctype = epg_genre_get_name(de->de_content_type, 0);
|
||||
} else if (ee && ee->genre_cnt) {
|
||||
ctype = epg_genre_get_name(ee->genre[0], 0);
|
||||
if(de && de->de_content_type.code) {
|
||||
eg = &de->de_content_type;
|
||||
} else if (ee) {
|
||||
eg = LIST_FIRST(&ee->genre);
|
||||
}
|
||||
if(ctype != NULL)
|
||||
if(eg && epg_genre_get_str(eg, 1, 0, ctype, 100))
|
||||
addtag(q, build_tag_string("CONTENT_TYPE", ctype, 0, NULL));
|
||||
|
||||
if(ch)
|
||||
|
|
250
src/epg.c
250
src/epg.c
|
@ -343,33 +343,33 @@ int epg_brand_set_season_count ( epg_brand_t *brand, uint16_t count )
|
|||
static void _epg_brand_add_season
|
||||
( epg_brand_t *brand, epg_season_t *season )
|
||||
{
|
||||
LIST_INSERT_SORTED(&brand->seasons, season, blink, _season_order);
|
||||
_epg_object_getref(brand);
|
||||
_epg_object_set_updated(brand);
|
||||
LIST_INSERT_SORTED(&brand->seasons, season, blink, _season_order);
|
||||
}
|
||||
|
||||
static void _epg_brand_rem_season
|
||||
( epg_brand_t *brand, epg_season_t *season )
|
||||
{
|
||||
LIST_REMOVE(season, blink);
|
||||
_epg_object_putref(brand);
|
||||
_epg_object_set_updated(brand);
|
||||
_epg_object_putref(brand);
|
||||
}
|
||||
|
||||
static void _epg_brand_add_episode
|
||||
( epg_brand_t *brand, epg_episode_t *episode )
|
||||
{
|
||||
LIST_INSERT_SORTED(&brand->episodes, episode, blink, _episode_order);
|
||||
_epg_object_getref(brand);
|
||||
_epg_object_set_updated(brand);
|
||||
LIST_INSERT_SORTED(&brand->episodes, episode, blink, _episode_order);
|
||||
}
|
||||
|
||||
static void _epg_brand_rem_episode
|
||||
( epg_brand_t *brand, epg_episode_t *episode )
|
||||
{
|
||||
LIST_REMOVE(episode, blink);
|
||||
_epg_object_putref(brand);
|
||||
_epg_object_set_updated(brand);
|
||||
_epg_object_putref(brand);
|
||||
}
|
||||
|
||||
htsmsg_t *epg_brand_serialize ( epg_brand_t *brand )
|
||||
|
@ -509,17 +509,17 @@ int epg_season_set_brand ( epg_season_t *season, epg_brand_t *brand, int u )
|
|||
static void _epg_season_add_episode
|
||||
( epg_season_t *season, epg_episode_t *episode )
|
||||
{
|
||||
LIST_INSERT_SORTED(&season->episodes, episode, slink, _episode_order);
|
||||
_epg_object_getref(season);
|
||||
_epg_object_set_updated(season);
|
||||
LIST_INSERT_SORTED(&season->episodes, episode, slink, _episode_order);
|
||||
}
|
||||
|
||||
static void _epg_season_rem_episode
|
||||
( epg_season_t *season, epg_episode_t *episode )
|
||||
{
|
||||
LIST_REMOVE(episode, slink);
|
||||
_epg_object_putref(season);
|
||||
_epg_object_set_updated(season);
|
||||
_epg_object_putref(season);
|
||||
}
|
||||
|
||||
htsmsg_t *epg_season_serialize ( epg_season_t *season )
|
||||
|
@ -618,6 +618,7 @@ static epg_episode_num_t *epg_episode_num_deserialize
|
|||
|
||||
static void _epg_episode_destroy ( void *eo )
|
||||
{
|
||||
epg_genre_t *g;
|
||||
epg_episode_t *ee = eo;
|
||||
if (LIST_FIRST(&ee->broadcasts)) {
|
||||
tvhlog(LOG_CRIT, "epg", "attempt to destroy episode with broadcasts");
|
||||
|
@ -630,7 +631,10 @@ static void _epg_episode_destroy ( void *eo )
|
|||
if (ee->subtitle) free(ee->subtitle);
|
||||
if (ee->summary) free(ee->summary);
|
||||
if (ee->description) free(ee->description);
|
||||
if (ee->genre) free(ee->genre);
|
||||
while ((g = LIST_FIRST(&ee->genre))) {
|
||||
LIST_REMOVE(g, link);
|
||||
free(g);
|
||||
}
|
||||
if (ee->image) free(ee->image);
|
||||
if (ee->epnum.text) free(ee->epnum.text);
|
||||
free(ee);
|
||||
|
@ -761,69 +765,52 @@ int epg_episode_set_season ( epg_episode_t *episode, epg_season_t *season )
|
|||
return save;
|
||||
}
|
||||
|
||||
int epg_episode_set_genre ( epg_episode_t *ee, const uint8_t *genre, int cnt )
|
||||
int epg_episode_set_genre ( epg_episode_t *ee, epg_genre_list_t *genre )
|
||||
{
|
||||
int i, save = 0;
|
||||
if (!ee || !genre || !cnt) return 0;
|
||||
if (cnt != ee->genre_cnt)
|
||||
save = 1;
|
||||
else {
|
||||
for (i = 0; i < cnt; i++ ) {
|
||||
if (genre[i] != ee->genre[i]) {
|
||||
save = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (save) {
|
||||
if (cnt > ee->genre_cnt)
|
||||
ee->genre = realloc(ee->genre, cnt * sizeof(uint8_t));
|
||||
memcpy(ee->genre, genre, cnt * sizeof(uint8_t));
|
||||
ee->genre_cnt = cnt;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
int save = 0;
|
||||
epg_genre_t *g1, *g2;
|
||||
|
||||
// Note: only works for the EN 300 468 defined names
|
||||
int epg_episode_set_genre_str ( epg_episode_t *ee, const char **gstr )
|
||||
{
|
||||
static int gcnt = 0;
|
||||
static uint8_t *genre;
|
||||
int cnt = 0;
|
||||
while (gstr[cnt]) cnt++;
|
||||
if (!cnt) return 0;
|
||||
if (cnt > gcnt) {
|
||||
genre = realloc(genre, sizeof(uint8_t) * cnt);
|
||||
gcnt = cnt;
|
||||
/* Remove old */
|
||||
g1 = LIST_FIRST(&ee->genre);
|
||||
while (g1) {
|
||||
g2 = LIST_NEXT(g1, link);
|
||||
if (!epg_genre_list_contains(genre, g1, 0)) {
|
||||
LIST_REMOVE(g1, link);
|
||||
save = 1;
|
||||
}
|
||||
g1 = g2;
|
||||
}
|
||||
cnt = 0;
|
||||
while (gstr[cnt]) {
|
||||
genre[cnt] = epg_genre_find_by_name(gstr[cnt]);
|
||||
cnt++;
|
||||
|
||||
/* Insert all entries */
|
||||
LIST_FOREACH(g1, genre, link) {
|
||||
save |= epg_genre_list_add(&ee->genre, g1);
|
||||
}
|
||||
return epg_episode_set_genre(ee, genre, gcnt);
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
int epg_episode_set_is_bw ( epg_episode_t *e, uint8_t bw )
|
||||
{
|
||||
int save = 0;
|
||||
if (!e) return 0;
|
||||
return _epg_object_set_u8(e, &e->is_bw, bw);
|
||||
return save;
|
||||
}
|
||||
|
||||
static void _epg_episode_add_broadcast
|
||||
( epg_episode_t *episode, epg_broadcast_t *broadcast )
|
||||
{
|
||||
LIST_INSERT_SORTED(&episode->broadcasts, broadcast, ep_link, _ebc_start_cmp);
|
||||
_epg_object_getref(episode);
|
||||
_epg_object_set_updated(episode);
|
||||
LIST_INSERT_SORTED(&episode->broadcasts, broadcast, ep_link, _ebc_start_cmp);
|
||||
}
|
||||
|
||||
static void _epg_episode_rem_broadcast
|
||||
( epg_episode_t *episode, epg_broadcast_t *broadcast )
|
||||
{
|
||||
LIST_REMOVE(broadcast, ep_link);
|
||||
_epg_object_putref(episode);
|
||||
_epg_object_set_updated(episode);
|
||||
_epg_object_putref(episode);
|
||||
}
|
||||
|
||||
size_t epg_episode_number_format
|
||||
|
@ -893,7 +880,8 @@ int epg_episode_fuzzy_match
|
|||
|
||||
htsmsg_t *epg_episode_serialize ( epg_episode_t *episode )
|
||||
{
|
||||
htsmsg_t *m;
|
||||
epg_genre_t *eg;
|
||||
htsmsg_t *m, *a = NULL;
|
||||
if (!episode || !episode->uri) return NULL;
|
||||
if (!(m = _epg_object_serialize((epg_object_t*)episode))) return NULL;
|
||||
htsmsg_add_str(m, "uri", episode->uri);
|
||||
|
@ -905,7 +893,12 @@ htsmsg_t *epg_episode_serialize ( epg_episode_t *episode )
|
|||
htsmsg_add_str(m, "summary", episode->summary);
|
||||
if (episode->description)
|
||||
htsmsg_add_str(m, "description", episode->description);
|
||||
htsmsg_add_msg(m, "epnum", epg_episode_num_serialize(&episode->epnum));
|
||||
htsmsg_add_msg(m, "epnum", epg_episode_num_serialize(&episode->epnum));
|
||||
LIST_FOREACH(eg, &episode->genre, link) {
|
||||
if (!a) a = htsmsg_create_list();
|
||||
htsmsg_add_u32(a, NULL, eg->code);
|
||||
}
|
||||
if (a) htsmsg_add_msg(m, "genre", a);
|
||||
if (episode->brand)
|
||||
htsmsg_add_str(m, "brand", episode->brand->uri);
|
||||
if (episode->season)
|
||||
|
@ -924,6 +917,7 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
const char *str;
|
||||
epg_episode_num_t num;
|
||||
htsmsg_t *sub;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
if ( !_epg_object_deserialize(m, *skel) ) return NULL;
|
||||
if ( !(ee = epg_episode_find_by_uri((*skel)->uri, create, save)) ) return NULL;
|
||||
|
@ -941,6 +935,17 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
*save |= epg_episode_set_epnum(ee, &num);
|
||||
if (num.text) free(num.text);
|
||||
}
|
||||
if ( (sub = htsmsg_get_list(m, "genre")) ) {
|
||||
epg_genre_list_t *egl = calloc(1, sizeof(epg_genre_list_t));
|
||||
HTSMSG_FOREACH(f, sub) {
|
||||
epg_genre_t genre;
|
||||
genre.code = (uint8_t)f->hmf_s64;
|
||||
epg_genre_list_add(egl, &genre);
|
||||
}
|
||||
*save |= epg_episode_set_genre(ee, egl);
|
||||
epg_genre_list_destroy(egl);
|
||||
}
|
||||
|
||||
if ( (str = htsmsg_get_str(m, "season")) )
|
||||
if ( (es = epg_season_find_by_uri(str, 0, NULL)) )
|
||||
*save |= epg_episode_set_season(ee, es);
|
||||
|
@ -1045,6 +1050,7 @@ static epg_broadcast_t *_epg_channel_add_broadcast
|
|||
ret = *bcast;
|
||||
*bcast = NULL;
|
||||
_epg_object_create(ret);
|
||||
// Note: sets updated
|
||||
_epg_object_getref(ret);
|
||||
|
||||
/* No change */
|
||||
|
@ -1054,6 +1060,7 @@ static epg_broadcast_t *_epg_channel_add_broadcast
|
|||
/* Extend in time */
|
||||
} else {
|
||||
ret->stop = (*bcast)->stop;
|
||||
_epg_object_set_updated(ret);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1334,11 +1341,12 @@ epg_broadcast_t *epg_broadcast_deserialize
|
|||
* Genre
|
||||
* *************************************************************************/
|
||||
|
||||
// TODO: make this configurable
|
||||
// FULL(ish) list from EN 300 468, I've excluded the last category
|
||||
// that relates more to broadcast content than what I call a "genre"
|
||||
// these will be handled elsewhere as broadcast metadata
|
||||
static const char *_epg_genre_names[16][16] = {
|
||||
{},
|
||||
{ "" },
|
||||
{
|
||||
"Movie/Drama",
|
||||
"detective/thriller",
|
||||
|
@ -1505,7 +1513,7 @@ static int _genre_str_match ( const char *a, const char *b )
|
|||
return (a[i] == '\0' && b[j] == '\0'); // end of string(both)
|
||||
}
|
||||
|
||||
uint8_t epg_genre_find_by_name ( const char *name )
|
||||
static uint8_t _epg_genre_find_by_name ( const char *name )
|
||||
{
|
||||
uint8_t a, b;
|
||||
for ( a = 1; a < 11; a++ ) {
|
||||
|
@ -1517,12 +1525,131 @@ uint8_t epg_genre_find_by_name ( const char *name )
|
|||
return 0; // undefined
|
||||
}
|
||||
|
||||
const char *epg_genre_get_name ( uint8_t genre, int full )
|
||||
uint8_t epg_genre_get_eit ( const epg_genre_t *genre )
|
||||
{
|
||||
int a, b = 0;
|
||||
a = (genre >> 4) & 0xF;
|
||||
if (full) b = (genre & 0xF);
|
||||
return _epg_genre_names[a][b];
|
||||
if (!genre) return 0;
|
||||
return genre->code;
|
||||
}
|
||||
|
||||
size_t epg_genre_get_str ( const epg_genre_t *genre, int major_only,
|
||||
int major_prefix, char *buf, size_t len )
|
||||
{
|
||||
int maj, min;
|
||||
size_t ret = 0;
|
||||
if (!genre || !buf) return 0;
|
||||
maj = (genre->code >> 4) & 0xf;
|
||||
if (!_epg_genre_names[maj][0]) return 0;
|
||||
min = major_only ? 0 : (genre->code & 0xf);
|
||||
if (!min || major_prefix ) {
|
||||
ret = snprintf(buf, len, "%s", _epg_genre_names[maj][0]);
|
||||
if (min) ret += snprintf(buf+ret, len-ret, " : ");
|
||||
}
|
||||
if (min && _epg_genre_names[maj][min]) {
|
||||
ret += snprintf(buf+ret, len-ret, "%s", _epg_genre_names[maj][min]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int epg_genre_list_add ( epg_genre_list_t *list, epg_genre_t *genre )
|
||||
{
|
||||
epg_genre_t *g1, *g2;
|
||||
if (!list || !genre || !genre->code) return 0;
|
||||
g1 = LIST_FIRST(list);
|
||||
if (!g1) {
|
||||
g2 = calloc(1, sizeof(epg_genre_t));
|
||||
g2->code = genre->code;
|
||||
LIST_INSERT_HEAD(list, g2, link);
|
||||
} else {
|
||||
while (g1) {
|
||||
|
||||
/* Already exists */
|
||||
if (g1->code == genre->code) return 0;
|
||||
|
||||
/* Update a major only entry */
|
||||
if (g1->code == (genre->code & 0xF0)) {
|
||||
g1->code = genre->code;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Insert before */
|
||||
if (g1->code > genre->code) {
|
||||
g2 = calloc(1, sizeof(epg_genre_t));
|
||||
g2->code = genre->code;
|
||||
LIST_INSERT_BEFORE(g1, g2, link);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Insert after (end) */
|
||||
if (!(g2 = LIST_NEXT(g1, link))) {
|
||||
g2 = calloc(1, sizeof(epg_genre_t));
|
||||
g2->code = genre->code;
|
||||
LIST_INSERT_AFTER(g1, g2, link);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Next */
|
||||
g1 = g2;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int epg_genre_list_add_by_eit ( epg_genre_list_t *list, uint8_t eit )
|
||||
{
|
||||
epg_genre_t g;
|
||||
g.code = eit;
|
||||
return epg_genre_list_add(list, &g);
|
||||
}
|
||||
|
||||
int epg_genre_list_add_by_str ( epg_genre_list_t *list, const char *str )
|
||||
{
|
||||
epg_genre_t g;
|
||||
g.code = _epg_genre_find_by_name(str);
|
||||
return epg_genre_list_add(list, &g);
|
||||
}
|
||||
|
||||
// Note: if partial=1 and genre is a major only category then all minor
|
||||
// entries will also match
|
||||
int epg_genre_list_contains
|
||||
( epg_genre_list_t *list, epg_genre_t *genre, int partial )
|
||||
{
|
||||
uint8_t mask = 0xFF;
|
||||
epg_genre_t *g;
|
||||
if (!list || !genre) return 0;
|
||||
if (partial && !(genre->code & 0x0F)) mask = 0xF0;
|
||||
LIST_FOREACH(g, list, link) {
|
||||
if ((g->code & mask) == genre->code) break;
|
||||
}
|
||||
return g ? 1 : 0;
|
||||
}
|
||||
|
||||
void epg_genre_list_destroy ( epg_genre_list_t *list )
|
||||
{
|
||||
epg_genre_t *g;
|
||||
while ((g = LIST_FIRST(list))) {
|
||||
LIST_REMOVE(g, link);
|
||||
free(g);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
|
||||
htsmsg_t *epg_genres_list_all ( int major_only, int major_prefix )
|
||||
{
|
||||
int i, j;
|
||||
htsmsg_t *e, *m;
|
||||
m = htsmsg_create_list();
|
||||
for (i = 0; i < 16; i++ ) {
|
||||
for (j = 0; j < (major_only ? 1 : 16); j++) {
|
||||
if (_epg_genre_names[i][j]) {
|
||||
e = htsmsg_create_map();
|
||||
htsmsg_add_u32(e, "code", i << 4 | j);
|
||||
htsmsg_add_str(e, "name", _epg_genre_names[i][j]);
|
||||
// TODO: use major_prefix
|
||||
htsmsg_add_msg(m, NULL, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
|
@ -1531,12 +1658,12 @@ const char *epg_genre_get_name ( uint8_t genre, int full )
|
|||
|
||||
static void _eqr_add
|
||||
( epg_query_result_t *eqr, epg_broadcast_t *e,
|
||||
uint8_t genre, regex_t *preg, time_t start )
|
||||
epg_genre_t *genre, regex_t *preg, time_t start )
|
||||
{
|
||||
/* Ignore */
|
||||
if ( e->stop < start ) return;
|
||||
if ( genre && e->episode->genre_cnt && e->episode->genre[0] != genre ) return;
|
||||
if ( !e->episode->title ) return;
|
||||
if ( genre && !epg_genre_list_contains(&e->episode->genre, genre, 1) ) return;
|
||||
if ( preg && regexec(preg, e->episode->title, 0, NULL, 0) ) return;
|
||||
|
||||
/* More space */
|
||||
|
@ -1551,7 +1678,7 @@ static void _eqr_add
|
|||
}
|
||||
|
||||
static void _eqr_add_channel
|
||||
( epg_query_result_t *eqr, channel_t *ch, uint8_t genre,
|
||||
( epg_query_result_t *eqr, channel_t *ch, epg_genre_t *genre,
|
||||
regex_t *preg, time_t start )
|
||||
{
|
||||
epg_broadcast_t *ebc;
|
||||
|
@ -1562,7 +1689,7 @@ static void _eqr_add_channel
|
|||
|
||||
void epg_query0
|
||||
( epg_query_result_t *eqr, channel_t *channel, channel_tag_t *tag,
|
||||
uint8_t genre, const char *title )
|
||||
epg_genre_t *genre, const char *title )
|
||||
{
|
||||
time_t now;
|
||||
channel_tag_mapping_t *ctm;
|
||||
|
@ -1604,12 +1731,11 @@ void epg_query0
|
|||
}
|
||||
|
||||
void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
|
||||
const char *genre, const char *title)
|
||||
epg_genre_t *genre, const char *title)
|
||||
{
|
||||
channel_t *ch = channel ? channel_find_by_name(channel, 0, 0) : NULL;
|
||||
channel_tag_t *ct = tag ? channel_tag_find_by_name(tag, 0) : NULL;
|
||||
uint8_t ge = genre ? epg_genre_find_by_name(genre) : 0;
|
||||
epg_query0(eqr, ch, ct, ge, title);
|
||||
epg_query0(eqr, ch, ct, genre, title);
|
||||
}
|
||||
|
||||
void epg_query_free(epg_query_result_t *eqr)
|
||||
|
|
55
src/epg.h
55
src/epg.h
|
@ -28,7 +28,7 @@ struct channel;
|
|||
struct channel_tag;
|
||||
|
||||
/*
|
||||
* Map types
|
||||
* Map/List types
|
||||
*/
|
||||
LIST_HEAD(epg_object_list, epg_object);
|
||||
RB_HEAD (epg_object_tree, epg_object);
|
||||
|
@ -37,10 +37,12 @@ LIST_HEAD(epg_season_list, epg_season);
|
|||
LIST_HEAD(epg_episode_list, epg_episode);
|
||||
LIST_HEAD(epg_broadcast_list, epg_broadcast);
|
||||
RB_HEAD (epg_broadcast_tree, epg_broadcast);
|
||||
LIST_HEAD(epg_genre_list, epg_genre);
|
||||
|
||||
/*
|
||||
* Typedefs (most are redundant!)
|
||||
*/
|
||||
typedef struct epg_genre epg_genre_t;
|
||||
typedef struct epg_object epg_object_t;
|
||||
typedef struct epg_brand epg_brand_t;
|
||||
typedef struct epg_season epg_season_t;
|
||||
|
@ -52,6 +54,38 @@ typedef struct epg_broadcast_list epg_broadcast_list_t;
|
|||
typedef struct epg_broadcast_tree epg_broadcast_tree_t;
|
||||
typedef struct epg_object_list epg_object_list_t;
|
||||
typedef struct epg_object_tree epg_object_tree_t;
|
||||
typedef struct epg_genre_list epg_genre_list_t;
|
||||
|
||||
/* ************************************************************************
|
||||
* Genres
|
||||
* ***********************************************************************/
|
||||
|
||||
/* Genre object */
|
||||
struct epg_genre
|
||||
{
|
||||
LIST_ENTRY(epg_genre) link;
|
||||
uint8_t code;
|
||||
};
|
||||
|
||||
/* Accessors */
|
||||
uint8_t epg_genre_get_eit ( const epg_genre_t *genre );
|
||||
size_t epg_genre_get_str ( const epg_genre_t *genre, int major_only,
|
||||
int major_prefix, char *buf, size_t len );
|
||||
|
||||
/* Delete */
|
||||
void epg_genre_list_destroy ( epg_genre_list_t *list );
|
||||
|
||||
/* Add to list */
|
||||
int epg_genre_list_add ( epg_genre_list_t *list, epg_genre_t *genre );
|
||||
int epg_genre_list_add_by_eit ( epg_genre_list_t *list, uint8_t eit );
|
||||
int epg_genre_list_add_by_str ( epg_genre_list_t *list, const char *str );
|
||||
|
||||
/* Search */
|
||||
int epg_genre_list_contains
|
||||
( epg_genre_list_t *list, epg_genre_t *genre, int partial );
|
||||
|
||||
/* List all available genres */
|
||||
htsmsg_t *epg_genres_list_all ( int major_only, int major_prefix );
|
||||
|
||||
/* ************************************************************************
|
||||
* Generic Object
|
||||
|
@ -198,11 +232,10 @@ struct epg_episode
|
|||
char *subtitle; ///< Sub-title
|
||||
char *summary; ///< Summary
|
||||
char *description; ///< An extended description
|
||||
uint8_t *genre; ///< Episode genre(s)
|
||||
int genre_cnt; ///< Genre count
|
||||
char *image; ///< Episode image
|
||||
epg_genre_list_t genre; ///< Episode genre(s)
|
||||
epg_episode_num_t epnum; ///< Episode numbering
|
||||
// Note: do not use epnum directly! use the accessor routine
|
||||
char *image; ///< Episode image
|
||||
|
||||
uint8_t is_bw; ///< Is black and white
|
||||
// TODO: certification and rating
|
||||
|
@ -213,7 +246,6 @@ struct epg_episode
|
|||
epg_brand_t *brand; ///< (Grand-)Parent brand
|
||||
epg_season_t *season; ///< Parent season
|
||||
epg_broadcast_list_t broadcasts; ///< Broadcast list
|
||||
|
||||
};
|
||||
|
||||
/* Lookup */
|
||||
|
@ -241,7 +273,7 @@ int epg_episode_set_brand ( epg_episode_t *e, epg_brand_t *b )
|
|||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_season ( epg_episode_t *e, epg_season_t *s )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_genre ( epg_episode_t *e, const uint8_t *g, int c )
|
||||
int epg_episode_set_genre ( epg_episode_t *e, epg_genre_list_t *g )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_genre_str ( epg_episode_t *e, const char **s )
|
||||
__attribute__((warn_unused_result));
|
||||
|
@ -359,13 +391,6 @@ epg_broadcast_t *epg_broadcast_deserialize
|
|||
/* Unlink */
|
||||
void epg_channel_unlink ( struct channel *ch );
|
||||
|
||||
/* ************************************************************************
|
||||
* Genre
|
||||
* ***********************************************************************/
|
||||
|
||||
uint8_t epg_genre_find_by_name ( const char *name );
|
||||
const char *epg_genre_get_name ( uint8_t genre, int full );
|
||||
|
||||
/* ************************************************************************
|
||||
* Querying
|
||||
* ***********************************************************************/
|
||||
|
@ -387,9 +412,9 @@ 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, uint8_t type, const char *title);
|
||||
struct channel_tag *ct, epg_genre_t *genre, const char *title);
|
||||
void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag,
|
||||
const char *contentgroup, const char *title);
|
||||
epg_genre_t *genre, const char *title);
|
||||
|
||||
|
||||
/* ************************************************************************
|
||||
|
|
|
@ -61,12 +61,18 @@ typedef struct epggrab_stats
|
|||
* Grabber Channels
|
||||
* *************************************************************************/
|
||||
|
||||
/*
|
||||
* Lists
|
||||
*/
|
||||
RB_HEAD(epggrab_channel_tree, epggrab_channel);
|
||||
typedef struct epggrab_channel_tree epggrab_channel_tree_t;
|
||||
|
||||
/*
|
||||
* Grab channel
|
||||
*/
|
||||
typedef struct epggrab_channel
|
||||
{
|
||||
RB_ENTRY(epggrab_channel) link; ///< Global link
|
||||
RB_ENTRY(epggrab_channel) link; ///< Global link
|
||||
epggrab_module_t *mod; ///< Linked module
|
||||
|
||||
char *id; ///< Grabber's ID
|
||||
|
@ -74,19 +80,10 @@ typedef struct epggrab_channel
|
|||
char *name; ///< Channel name
|
||||
char *icon; ///< Channel icon
|
||||
int number; ///< Channel number
|
||||
|
||||
char **sname; ///< Service name's
|
||||
uint16_t *sid; ///< Service ID's
|
||||
|
||||
|
||||
struct channel *channel; ///< Mapped channel
|
||||
} epggrab_channel_t;
|
||||
|
||||
/*
|
||||
* Channel list structure
|
||||
*/
|
||||
RB_HEAD(epggrab_channel_tree, epggrab_channel);
|
||||
typedef struct epggrab_channel_tree epggrab_channel_tree_t;
|
||||
|
||||
/*
|
||||
* Access functions
|
||||
*/
|
||||
|
@ -95,11 +92,9 @@ htsmsg_t* epggrab_channel_list ( void );
|
|||
/*
|
||||
* Mutators
|
||||
*/
|
||||
int epggrab_channel_set_name ( epggrab_channel_t *ch, const char *name );
|
||||
int epggrab_channel_set_icon ( epggrab_channel_t *ch, const char *icon );
|
||||
int epggrab_channel_set_number ( epggrab_channel_t *ch, int number );
|
||||
int epggrab_channel_set_sname ( epggrab_channel_t *ch, const char **sname );
|
||||
int epggrab_channel_set_sid ( epggrab_channel_t *ch, const uint16_t *sid );
|
||||
int epggrab_channel_set_name ( epggrab_channel_t *ch, const char *name );
|
||||
int epggrab_channel_set_icon ( epggrab_channel_t *ch, const char *icon );
|
||||
int epggrab_channel_set_number ( epggrab_channel_t *ch, int number );
|
||||
|
||||
/*
|
||||
* Updated/link
|
||||
|
|
|
@ -36,39 +36,13 @@
|
|||
// returns 1 if link made
|
||||
int epggrab_channel_link ( epggrab_channel_t *ec, channel_t *ch )
|
||||
{
|
||||
service_t *sv;
|
||||
int match = 0, i;
|
||||
int match = 0;
|
||||
|
||||
if (!ec || !ch) return 0;
|
||||
if (ec->channel) return 0;
|
||||
|
||||
if (ec->name && !strcmp(ec->name, ch->ch_name))
|
||||
match = 1;
|
||||
else {
|
||||
LIST_FOREACH(sv, &ch->ch_services, s_ch_link) {
|
||||
if (ec->sid) {
|
||||
i = 0;
|
||||
while (ec->sid[i]) {
|
||||
if (sv->s_dvb_service_id == ec->sid[i]) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (!match && ec->sname) {
|
||||
i = 0;
|
||||
while (ec->sname[i]) {
|
||||
if (!strcmp(ec->sname[i], sv->s_svcname)) {
|
||||
match = 1;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (match) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
tvhlog(LOG_INFO, ec->mod->id, "linking %s to %s",
|
||||
|
@ -129,63 +103,6 @@ int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
|
|||
return save;
|
||||
}
|
||||
|
||||
/* Set service IDs */
|
||||
int epggrab_channel_set_sid
|
||||
( epggrab_channel_t *ec, const uint16_t *sid )
|
||||
{
|
||||
int save = 0, i;
|
||||
if ( !ec || !sid ) return 0;
|
||||
if (!ec->sid) save = 1;
|
||||
else {
|
||||
i = 0;
|
||||
while ( ec->sid[i] && sid[i] ) {
|
||||
if ( ec->sid[i] != sid[i] ) break;
|
||||
i++;
|
||||
}
|
||||
if (ec->sid[i] || sid[i]) save = 1;
|
||||
}
|
||||
if (save) {
|
||||
i = 0;
|
||||
while (ec->sid[i++]);
|
||||
if (ec->sid) free(ec->sid);
|
||||
ec->sid = calloc(i, sizeof(uint16_t));
|
||||
memcpy(ec->sid, sid, i * sizeof(uint16_t));
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/* Set names */
|
||||
int epggrab_channel_set_sname ( epggrab_channel_t *ec, const char **sname )
|
||||
{
|
||||
int save = 0, i = 0;
|
||||
if ( !ec || !sname ) return 0;
|
||||
if (!ec->sname) save = 1;
|
||||
else {
|
||||
while ( ec->sname[i] && sname[i] ) {
|
||||
if (strcmp(ec->sname[i], sname[i])) break;
|
||||
i++;
|
||||
}
|
||||
if (ec->sname[i] || sname[i]) save = 1;
|
||||
}
|
||||
if (save) {
|
||||
if (ec->sname) {
|
||||
i = 0;
|
||||
while (ec->sname[i])
|
||||
free(ec->sname[i++]);
|
||||
free(ec->sname);
|
||||
}
|
||||
i = 0;
|
||||
while (sname[i++]);
|
||||
ec->sname = calloc(i+1, sizeof(char*));
|
||||
i = 0;
|
||||
while (sname[i]) {
|
||||
ec->sname[i] = strdup(sname[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/* Channel settings updated */
|
||||
void epggrab_channel_updated ( epggrab_channel_t *ec )
|
||||
{
|
||||
|
|
|
@ -142,9 +142,7 @@ void epggrab_module_parse
|
|||
|
||||
void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch )
|
||||
{
|
||||
int i;
|
||||
htsmsg_t *m = htsmsg_create_map();
|
||||
htsmsg_t *a;
|
||||
epggrab_module_t *mod = _m;
|
||||
|
||||
if (ch->name)
|
||||
|
@ -153,24 +151,6 @@ void epggrab_module_ch_save ( void *_m, epggrab_channel_t *ch )
|
|||
htsmsg_add_str(m, "icon", ch->icon);
|
||||
if (ch->channel)
|
||||
htsmsg_add_u32(m, "channel", ch->channel->ch_id);
|
||||
if (ch->sid) {
|
||||
a = htsmsg_create_list();
|
||||
i = 0;
|
||||
while (ch->sid[i]) {
|
||||
htsmsg_add_u32(a, NULL, ch->sid[i]);
|
||||
i++;
|
||||
}
|
||||
htsmsg_add_msg(m, "sid", a);
|
||||
}
|
||||
if (ch->sname) {
|
||||
a = htsmsg_create_list();
|
||||
i = 0;
|
||||
while (ch->sname[i]) {
|
||||
htsmsg_add_str(a, NULL, ch->sname[i]);
|
||||
i++;
|
||||
}
|
||||
htsmsg_add_msg(m, "sname", a);
|
||||
}
|
||||
if (ch->number)
|
||||
htsmsg_add_u32(m, "number", ch->number);
|
||||
|
||||
|
@ -210,11 +190,9 @@ void epggrab_module_ch_mod ( void *mod, channel_t *ch )
|
|||
static void _epggrab_module_channel_load
|
||||
( epggrab_module_t *mod, htsmsg_t *m, const char *id )
|
||||
{
|
||||
int save = 0, i;
|
||||
int save = 0;
|
||||
const char *str;
|
||||
uint32_t u32;
|
||||
htsmsg_t *a;
|
||||
htsmsg_field_t *f;
|
||||
|
||||
epggrab_channel_t *ch = epggrab_channel_find(mod->channels, id, 1, &save, mod);
|
||||
|
||||
|
@ -222,28 +200,6 @@ static void _epggrab_module_channel_load
|
|||
ch->name = strdup(str);
|
||||
if ((str = htsmsg_get_str(m, "icon")))
|
||||
ch->icon = strdup(str);
|
||||
if ((a = htsmsg_get_list(m, "sid"))) {
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) i++;
|
||||
if (i) {
|
||||
ch->sid = calloc(i+1, sizeof(uint16_t));
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) {
|
||||
ch->sid[i++] = (uint16_t)f->hmf_s64;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((a = htsmsg_get_list(m, "sname"))) {
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) i++;
|
||||
if (i) {
|
||||
ch->sname = calloc(i+1, sizeof(char*));
|
||||
i = 0;
|
||||
HTSMSG_FOREACH(f, a) {
|
||||
ch->sname[i++] = strdup(f->hmf_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!htsmsg_get_u32(m, "number", &u32))
|
||||
ch->number = u32;
|
||||
|
||||
|
|
|
@ -152,13 +152,12 @@ static int _eit_callback
|
|||
channel_t *ch;
|
||||
epg_broadcast_t *ebc;
|
||||
epg_episode_t *ee;
|
||||
epg_genre_list_t *egl = NULL;
|
||||
eit_status_t *sta;
|
||||
int resched = 0, save = 0, save2 = 0, dllen, dtag, dlen;
|
||||
uint16_t tsid, sid, eid;
|
||||
uint8_t bw, hd, ws, ad, ds, st;
|
||||
time_t start, stop;
|
||||
int genre_idx = 0;
|
||||
uint8_t genre[10];
|
||||
char title[256];
|
||||
char summary[256];
|
||||
char desc[5000];
|
||||
|
@ -260,7 +259,6 @@ static int _eit_callback
|
|||
/* Process tags */
|
||||
*title = *summary = *desc = 0;
|
||||
extra = NULL;
|
||||
genre_idx = 0;
|
||||
hd = ws = bw = ad = st = ds = 0;
|
||||
while(dllen > 0) {
|
||||
dtag = ptr[0];
|
||||
|
@ -296,8 +294,10 @@ static int _eit_callback
|
|||
dlen -= 2;
|
||||
if ( *ptr == 0xb1 )
|
||||
bw = 1;
|
||||
else if ( *ptr < 0xb0 && genre_idx < sizeof(genre) )
|
||||
genre[genre_idx++] = *ptr;
|
||||
else if ( *ptr < 0xb0 ) {
|
||||
if (!egl) egl = calloc(1, sizeof(epg_genre_list_t));
|
||||
epg_genre_list_add_by_eit(egl, *ptr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -376,8 +376,8 @@ static int _eit_callback
|
|||
char *uri;
|
||||
uri = epg_hash(title, summary, desc);
|
||||
if (uri) {
|
||||
ee = epg_episode_find_by_uri(uri, 1, &save2);
|
||||
save |= epg_broadcast_set_episode(ebc, ee);
|
||||
if ((ee = epg_episode_find_by_uri(uri, 1, &save2)))
|
||||
save |= epg_broadcast_set_episode(ebc, ee);
|
||||
free(uri);
|
||||
}
|
||||
}
|
||||
|
@ -392,14 +392,17 @@ static int _eit_callback
|
|||
save |= epg_episode_set_summary(ee, summary);
|
||||
if ( !ee->description && *desc )
|
||||
save |= epg_episode_set_description(ee, desc);
|
||||
if ( !ee->genre_cnt && genre_idx )
|
||||
save |= epg_episode_set_genre(ee, genre, genre_idx);
|
||||
if ( !LIST_FIRST(&ee->genre) && egl )
|
||||
save |= epg_episode_set_genre(ee, egl);
|
||||
#if TODO_ADD_EXTRA
|
||||
if ( extra )
|
||||
save |= epg_episode_set_extra(ee, extra);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Tidy up */
|
||||
if (extra) free(extra);
|
||||
if (egl) epg_genre_list_destroy(egl);
|
||||
}
|
||||
|
||||
/* Update EPG */
|
||||
|
|
|
@ -405,8 +405,12 @@ static int _opentv_parse_event_section
|
|||
save |= epg_episode_set_summary(ee, ev.summary);
|
||||
if (ev.desc)
|
||||
save |= epg_episode_set_description(ee, ev.desc);
|
||||
if (ev.cat)
|
||||
save |= epg_episode_set_genre(ee, &ev.cat, 1);
|
||||
if (ev.cat) {
|
||||
epg_genre_list_t *egl = calloc(1, sizeof(epg_genre_list_t));
|
||||
epg_genre_list_add_by_eit(egl, ev.cat);
|
||||
save |= epg_episode_set_genre(ee, egl);
|
||||
epg_genre_list_destroy(egl);
|
||||
}
|
||||
// Note: don't override the season (since the ID is channel specific
|
||||
// it'll keep changing!
|
||||
if (ev.series && !ee->season) {
|
||||
|
|
|
@ -55,23 +55,29 @@ static int _pyepg_parse_time ( const char *str, time_t *out )
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const uint8_t *_pyepg_parse_genre ( htsmsg_t *tags, int *cnt )
|
||||
static epg_genre_list_t
|
||||
*_pyepg_parse_genre ( htsmsg_t *tags )
|
||||
{
|
||||
// TODO: implement this
|
||||
return NULL;
|
||||
htsmsg_t *e;
|
||||
htsmsg_field_t *f;
|
||||
epg_genre_list_t *egl = NULL;
|
||||
HTSMSG_FOREACH(f, tags) {
|
||||
if (!strcmp(f->hmf_name, "genre") && (e = htsmsg_get_map_by_field(f))) {
|
||||
if (!egl) { egl = calloc(1, sizeof(epg_genre_list_t)); printf("alloc %p\n", egl); }
|
||||
printf("GENRE %s\n", htsmsg_get_str(e, "cdata"));
|
||||
epg_genre_list_add_by_str(egl, htsmsg_get_str(e, "cdata"));
|
||||
}
|
||||
}
|
||||
return egl;
|
||||
}
|
||||
|
||||
static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats )
|
||||
{
|
||||
int save = 0;
|
||||
epggrab_channel_t *ch;
|
||||
htsmsg_t *attr, *tags, *e;
|
||||
htsmsg_field_t *f;
|
||||
htsmsg_t *attr, *tags;
|
||||
const char *str;
|
||||
uint32_t u32;
|
||||
const char *sname[11];
|
||||
uint16_t sid[11];
|
||||
int sid_idx = 0, sname_idx = 0;
|
||||
|
||||
if ( data == NULL ) return 0;
|
||||
|
||||
|
@ -90,34 +96,6 @@ static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats )
|
|||
if ((!htsmsg_xml_get_cdata_u32(tags, "number", &u32)))
|
||||
save |= epggrab_channel_set_number(ch, u32);
|
||||
|
||||
HTSMSG_FOREACH(f, tags) {
|
||||
if (!strcmp(f->hmf_name, "sid")) {
|
||||
if (sid_idx < 10) {
|
||||
e = htsmsg_get_map_by_field(f);
|
||||
if (!htsmsg_get_u32(e, "cdata", &u32)) {
|
||||
sid[sid_idx] = (uint16_t)u32;
|
||||
sid_idx++;
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(f->hmf_name, "sname")) {
|
||||
if (sname_idx < 10) {
|
||||
e = htsmsg_get_map_by_field(f);
|
||||
if ((str = htsmsg_get_str(e, "cdata"))) {
|
||||
sname[sname_idx] = str;
|
||||
sname_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sid_idx) {
|
||||
sid[sid_idx] = 0;
|
||||
save |= epggrab_channel_set_sid(ch, sid);
|
||||
}
|
||||
if (sname_idx) {
|
||||
sname[sname_idx] = NULL;
|
||||
save |= epggrab_channel_set_sname(ch, sname);
|
||||
}
|
||||
|
||||
/* Update */
|
||||
if (save) {
|
||||
epggrab_channel_updated(ch);
|
||||
|
@ -229,14 +207,14 @@ static int _pyepg_parse_season ( htsmsg_t *data, epggrab_stats_t *stats )
|
|||
|
||||
static int _pyepg_parse_episode ( htsmsg_t *data, epggrab_stats_t *stats )
|
||||
{
|
||||
int genre_cnt, save = 0;
|
||||
int save = 0;
|
||||
htsmsg_t *attr, *tags;
|
||||
epg_episode_t *episode;
|
||||
epg_season_t *season;
|
||||
epg_brand_t *brand;
|
||||
const char *str;
|
||||
uint32_t u32, pc, pn;
|
||||
const uint8_t *genre;
|
||||
epg_genre_list_t *egl;
|
||||
|
||||
if ( data == NULL ) return 0;
|
||||
|
||||
|
@ -294,8 +272,9 @@ static int _pyepg_parse_episode ( htsmsg_t *data, epggrab_stats_t *stats )
|
|||
}
|
||||
|
||||
/* Genre */
|
||||
if ((genre = _pyepg_parse_genre(tags, &genre_cnt))) {
|
||||
save |= epg_episode_set_genre(episode, genre, genre_cnt);
|
||||
if ((egl = _pyepg_parse_genre(tags))) {
|
||||
save |= epg_episode_set_genre(episode, egl);
|
||||
epg_genre_list_destroy(egl);
|
||||
}
|
||||
|
||||
/* Content */
|
||||
|
|
|
@ -286,6 +286,24 @@ xmltv_parse_accessibility ( epg_broadcast_t *ebc, htsmsg_t *m )
|
|||
return save;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse category list
|
||||
*/
|
||||
static epg_genre_list_t
|
||||
*_xmltv_parse_categories ( htsmsg_t *tags )
|
||||
{
|
||||
htsmsg_t *e;
|
||||
htsmsg_field_t *f;
|
||||
epg_genre_list_t *egl = NULL;
|
||||
HTSMSG_FOREACH(f, tags) {
|
||||
if (!strcmp(f->hmf_name, "category") && (e = htsmsg_get_map_by_field(f))) {
|
||||
if (!egl) egl = calloc(1, sizeof(epg_genre_list_t));
|
||||
epg_genre_list_add_by_str(egl, htsmsg_get_str(e, "cdata"));
|
||||
}
|
||||
}
|
||||
return egl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse tags inside of a programme
|
||||
*/
|
||||
|
@ -296,12 +314,12 @@ _xmltv_parse_programme_tags(channel_t *ch, htsmsg_t *tags,
|
|||
int save = 0, save2 = 0;
|
||||
epg_episode_t *ee;
|
||||
epg_broadcast_t *ebc;
|
||||
epg_genre_list_t *egl;
|
||||
int sn = 0, sc = 0, en = 0, ec = 0, pn = 0, pc = 0;
|
||||
const char *onscreen = NULL;
|
||||
char *uri;
|
||||
const char *title = htsmsg_xml_get_cdata_str(tags, "title");
|
||||
const char *desc = htsmsg_xml_get_cdata_str(tags, "desc");
|
||||
const char *category[2];
|
||||
const char *title = htsmsg_xml_get_cdata_str(tags, "title");
|
||||
const char *desc = htsmsg_xml_get_cdata_str(tags, "desc");
|
||||
get_episode_info(tags, &onscreen, &sn, &sc, &en, &ec, &pn, &pc);
|
||||
|
||||
/* Ignore */
|
||||
|
@ -315,11 +333,12 @@ _xmltv_parse_programme_tags(channel_t *ch, htsmsg_t *tags,
|
|||
stats->episodes.total++;
|
||||
if (save) stats->episodes.created++;
|
||||
|
||||
category[0] = htsmsg_xml_get_cdata_str(tags, "category");
|
||||
category[1] = NULL;
|
||||
if (title) save |= epg_episode_set_title(ee, title);
|
||||
if (desc) save |= epg_episode_set_description(ee, desc);
|
||||
if (*category) save |= epg_episode_set_genre_str(ee, category);
|
||||
if ((egl = _xmltv_parse_categories(tags))) {
|
||||
save |= epg_episode_set_genre(ee, egl);
|
||||
epg_genre_list_destroy(egl);
|
||||
}
|
||||
if (pn) save |= epg_episode_set_part(ee, pn, pc);
|
||||
if (en) save |= epg_episode_set_number(ee, en);
|
||||
if (save) stats->episodes.modified++;
|
||||
|
|
16
src/htsp.c
16
src/htsp.c
|
@ -709,10 +709,11 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
htsmsg_t *out, *eventIds;
|
||||
const char *query;
|
||||
int c, i;
|
||||
uint32_t channelid, tagid, epg_content_dvbcode = 0;
|
||||
uint32_t channelid, tagid, epg_content_dvbcode;
|
||||
channel_t *ch = NULL;
|
||||
channel_tag_t *ct = NULL;
|
||||
epg_query_result_t eqr;
|
||||
epg_genre_t genre, *eg = NULL;
|
||||
|
||||
//only mandatory parameter is the query
|
||||
if( (query = htsmsg_get_str(in, "query")) == NULL )
|
||||
|
@ -724,10 +725,13 @@ htsp_method_epgQuery(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
if( !(htsmsg_get_u32(in, "tagId", &tagid)) )
|
||||
ct = channel_tag_find_by_identifier(tagid);
|
||||
|
||||
htsmsg_get_u32(in, "contentType", &epg_content_dvbcode);
|
||||
if (!htsmsg_get_u32(in, "contentType", &epg_content_dvbcode)) {
|
||||
genre.code = epg_content_dvbcode;
|
||||
eg = &genre;
|
||||
}
|
||||
|
||||
//do the query
|
||||
epg_query0(&eqr, ch, ct, epg_content_dvbcode, query);
|
||||
epg_query0(&eqr, ch, ct, eg, query);
|
||||
c = eqr.eqr_entries;
|
||||
|
||||
// create reply
|
||||
|
@ -754,6 +758,7 @@ htsp_build_event(epg_broadcast_t *e)
|
|||
htsmsg_t *out;
|
||||
epg_broadcast_t *n;
|
||||
dvr_entry_t *de;
|
||||
epg_genre_t *g;
|
||||
|
||||
out = htsmsg_create_map();
|
||||
|
||||
|
@ -769,9 +774,8 @@ htsp_build_event(epg_broadcast_t *e)
|
|||
else if(e->episode->summary != NULL)
|
||||
htsmsg_add_str(out, "description", e->episode->summary);
|
||||
|
||||
// TODO: only supports one entry!
|
||||
if(e->episode->genre_cnt)
|
||||
htsmsg_add_u32(out, "contentType", e->episode->genre[0]);
|
||||
if((g = LIST_FIRST(&e->episode->genre)))
|
||||
htsmsg_add_u32(out, "contentType", g->code);
|
||||
}
|
||||
|
||||
if((de = dvr_entry_find_by_event(e)) != NULL) {
|
||||
|
|
|
@ -414,24 +414,11 @@ static int
|
|||
extjs_ecglist(http_connection_t *hc, const char *remain, void *opaque)
|
||||
{
|
||||
htsbuf_queue_t *hq = &hc->hc_reply;
|
||||
htsmsg_t *out, *array, *c;
|
||||
const char *s;
|
||||
int i;
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = htsmsg_create_list();
|
||||
|
||||
for(i = 0; i < 16; i++) {
|
||||
if((s = epg_genre_get_name(i<<4, 0)) == NULL)
|
||||
continue;
|
||||
|
||||
c = htsmsg_create_map();
|
||||
htsmsg_add_str(c, "name", s);
|
||||
htsmsg_add_msg(array, NULL, c);
|
||||
}
|
||||
htsmsg_t *out, *array;
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = epg_genres_list_all(1, 0);
|
||||
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");
|
||||
|
@ -658,13 +645,13 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
|||
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 *cgrp = http_arg_get(&hc->hc_req_args, "contentgrp");
|
||||
const char *title = http_arg_get(&hc->hc_req_args, "title");
|
||||
|
||||
if(channel && !channel[0]) channel = NULL;
|
||||
|
@ -678,12 +665,17 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
|||
else
|
||||
limit = 20; /* XXX */
|
||||
|
||||
if ((s = http_arg_get(&hc->hc_req_args, "contenttype"))) {
|
||||
genre.code = atoi(s);
|
||||
eg = &genre;
|
||||
}
|
||||
|
||||
out = htsmsg_create_map();
|
||||
array = htsmsg_create_list();
|
||||
|
||||
pthread_mutex_lock(&global_lock);
|
||||
|
||||
epg_query(&eqr, channel, tag, cgrp, title);
|
||||
epg_query(&eqr, channel, tag, eg, title);
|
||||
|
||||
epg_query_sort(&eqr);
|
||||
|
||||
|
@ -724,9 +716,9 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
|||
htsmsg_add_u32(m, "end", e->stop);
|
||||
htsmsg_add_u32(m, "duration", e->stop - e->start);
|
||||
|
||||
if(ee->genre_cnt)
|
||||
if((s = epg_genre_get_name(ee->genre[0], 0)))
|
||||
htsmsg_add_str(m, "contentgrp", s);
|
||||
if((eg = LIST_FIRST(&ee->genre))) {
|
||||
htsmsg_add_u32(m, "contenttype", eg->code);
|
||||
}
|
||||
|
||||
dvr_entry_t *de;
|
||||
if((de = dvr_entry_find_by_event(e)) != NULL)
|
||||
|
@ -1000,13 +992,17 @@ extjs_dvr(http_connection_t *hc, const char *remain, void *opaque)
|
|||
htsmsg_add_u32(out, "success", 1);
|
||||
|
||||
} else if(!strcmp(op, "createAutoRec")) {
|
||||
const char *cgrp = http_arg_get(&hc->hc_req_args, "contentgrp");
|
||||
epg_genre_t genre, *eg = NULL;
|
||||
if ((s = http_arg_get(&hc->hc_req_args, "contenttype"))) {
|
||||
genre.code = atoi(s);
|
||||
eg = &genre;
|
||||
}
|
||||
|
||||
dvr_autorec_add(http_arg_get(&hc->hc_req_args, "config_name"),
|
||||
http_arg_get(&hc->hc_req_args, "title"),
|
||||
http_arg_get(&hc->hc_req_args, "channel"),
|
||||
http_arg_get(&hc->hc_req_args, "tag"),
|
||||
cgrp ? epg_genre_find_by_name(cgrp) : 0,
|
||||
eg,
|
||||
hc->hc_representative, "Created from EPG query");
|
||||
|
||||
out = htsmsg_create_map();
|
||||
|
|
|
@ -470,9 +470,13 @@ tvheadend.autoreceditor = function() {
|
|||
emptyText: 'Only include tag...'
|
||||
})
|
||||
},{
|
||||
header: "Content Group",
|
||||
dataIndex: 'contentgrp',
|
||||
header: "Genre",
|
||||
dataIndex: 'contenttype',
|
||||
renderer: function(v) {
|
||||
return tvheadend.contentGroupLookupName(v);
|
||||
},
|
||||
editor: new Ext.form.ComboBox({
|
||||
valueField: 'code',
|
||||
displayField:'name',
|
||||
store: tvheadend.ContentGroupStore,
|
||||
mode: 'local',
|
||||
|
@ -635,7 +639,7 @@ tvheadend.dvr = function() {
|
|||
|
||||
|
||||
tvheadend.autorecRecord = Ext.data.Record.create([
|
||||
'enabled','title', 'brand', 'channel','tag','creator','contentgrp','comment',
|
||||
'enabled','title', 'brand', 'channel','tag','creator','contenttype','comment',
|
||||
'weekdays', 'pri', 'approx_time', 'config_name'
|
||||
]);
|
||||
|
||||
|
|
|
@ -9,12 +9,25 @@ tvheadend.brands = new Ext.data.JsonStore({
|
|||
|
||||
tvheadend.ContentGroupStore = new Ext.data.JsonStore({
|
||||
root:'entries',
|
||||
fields: [{name: 'name'}],
|
||||
fields: ['name', 'code'],
|
||||
autoLoad: true,
|
||||
url:'ecglist'
|
||||
});
|
||||
|
||||
tvheadend.ContentGroupStore.setDefaultSort('name', 'ASC');
|
||||
tvheadend.contentGroupLookupName = function(code)
|
||||
{
|
||||
ret = "";
|
||||
tvheadend.ContentGroupStore.each(function(r)
|
||||
{
|
||||
if (r.data.code == code)
|
||||
ret = r.data.name;
|
||||
else if (ret == "" && r.data.code == code & 0xF0)
|
||||
ret = r.data.name;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
tvheadend.ContentGroupStore.setDefaultSort('code', 'ASC');
|
||||
|
||||
tvheadend.epgDetails = function(event) {
|
||||
|
||||
|
@ -29,7 +42,7 @@ tvheadend.epgDetails = function(event) {
|
|||
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.contentgrp + '</div>';
|
||||
content += '<div class="x-epg-meta">' + tvheadend.contentGroupLookupName(event.contenttype) + '</div>';
|
||||
|
||||
if(event.ext_desc != null)
|
||||
content += '<div class="x-epg-meta">' + event.ext_desc + '</div>';
|
||||
|
@ -206,7 +219,7 @@ tvheadend.epg = function() {
|
|||
{name: 'start', type: 'date', dateFormat: 'U' /* unix time */},
|
||||
{name: 'end', type: 'date', dateFormat: 'U' /* unix time */},
|
||||
{name: 'duration'},
|
||||
{name: 'contentgrp'},
|
||||
{name: 'contenttype'},
|
||||
{name: 'schedstate'}
|
||||
])
|
||||
});
|
||||
|
@ -298,10 +311,12 @@ tvheadend.epg = function() {
|
|||
renderer: renderText
|
||||
},{
|
||||
width: 250,
|
||||
id:'contentgrp',
|
||||
id:'contenttype',
|
||||
header: "Content Type",
|
||||
dataIndex: 'contentgrp',
|
||||
renderer: renderText
|
||||
dataIndex: 'contenttype',
|
||||
renderer: function(v) {
|
||||
return tvheadend.contentGroupLookupName(v);
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
|
@ -357,7 +372,7 @@ tvheadend.epg = function() {
|
|||
function epgQueryClear() {
|
||||
epgStore.baseParams.channel = null;
|
||||
epgStore.baseParams.tag = null;
|
||||
epgStore.baseParams.contentgrp = null;
|
||||
epgStore.baseParams.contenttype = null;
|
||||
epgStore.baseParams.title = null;
|
||||
|
||||
epgFilterChannels.setValue("");
|
||||
|
@ -383,8 +398,8 @@ tvheadend.epg = function() {
|
|||
});
|
||||
|
||||
epgFilterContentGroup.on('select', function(c, r) {
|
||||
if(epgStore.baseParams.contentgrp != r.data.name) {
|
||||
epgStore.baseParams.contentgrp = r.data.name;
|
||||
if(epgStore.baseParams.contenttype != r.data.code) {
|
||||
epgStore.baseParams.contenttype = r.data.code;
|
||||
epgStore.reload();
|
||||
}
|
||||
});
|
||||
|
@ -477,8 +492,8 @@ tvheadend.epg = function() {
|
|||
epgStore.baseParams.channel : "<i>Don't care</i>";
|
||||
var tag = epgStore.baseParams.tag ?
|
||||
epgStore.baseParams.tag : "<i>Don't care</i>";
|
||||
var contentgrp = epgStore.baseParams.contentgrp ?
|
||||
epgStore.baseParams.contentgrp : "<i>Don't care</i>";
|
||||
var contenttype = epgStore.baseParams.contenttype ?
|
||||
epgStore.baseParams.contenttype : "<i>Don't care</i>";
|
||||
|
||||
Ext.MessageBox.confirm('Auto Recorder',
|
||||
'This will create an automatic rule that ' +
|
||||
|
@ -488,7 +503,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">Content Group:</div>' + contentgrp + '<br>' +
|
||||
'<div class="x-smallhdr">Genre:</div>' + contenttype + '<br>' +
|
||||
'<br>' +
|
||||
'Currently this will match (and record) ' +
|
||||
epgStore.getTotalCount() + ' events. ' +
|
||||
|
|
Loading…
Add table
Reference in a new issue