Merge branch 'epg-metadata' into epg-rewrite
This commit is contained in:
commit
a31e2d303d
14 changed files with 482 additions and 139 deletions
58
docs/html/config_epggrab.html
Normal file
58
docs/html/config_epggrab.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
<div class="hts-doc-text">
|
||||
|
||||
<p>
|
||||
This tab is used to configure EPG grabbing capabilities. TVheadend supports
|
||||
a variety of different EPG grabbing mechanisms. These fall into 3 broad
|
||||
categories, within which there are a variety of specific grabber implementations.
|
||||
</p>
|
||||
|
||||
<h2>Grabber Types</h2>
|
||||
<ul>
|
||||
<li>Over the Air (OTA) - these receive EPG data directly from the DVB network. This is often the easiest way to get up and running, and does provide timely updates should scheduling change. However the information isn't always as rich as some of the other grabbers.</li>
|
||||
<li>Interanl - These are grabbers which can be internally initiated from within TVheadend based on IP based grabbing solutions (see below). This can be a quick way to get richer EPG data where you don't have decent OTA support.</li>
|
||||
<li>External - These provide the option to run the grabber scripts externally and to pump the data into TVheadend via Unix domain sockets. It provides more complex configurations using things like cronjob's etc.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Grabber Modules</h2>
|
||||
<ul>
|
||||
<li>EIT - This is a DVB standards compatible EIT grabber. Typically it will
|
||||
retrieve now/next information, though on some networks there may be more
|
||||
extensive data published.</li>
|
||||
<li>OpenTV - This is a proprietary OTA EPG grabber. Its known to be used on the SKY networks, but others may use it. You need two configuration files to define settings for your particular network, if you don't see yours listed please visit IRC #hts for help.</li>
|
||||
<li>XMLTV - This is a IP network based scraper, for more information about XMLTV please visit <a href="http://www.xmltv.org">http://www.xmltv.org</a>. To make use of the internal XMLTV grabber you must have tv_find_grabbers installed. If you install new grabbers you will need to restart TVheadend to pick these up asthey're loaded at startup.</li>
|
||||
<li>PyEPG - This is another IP network based scraper. It currently only supports the Atlas UK system (for which you need a key), but it does provide a very rich EPG data set. For more information see <a href='http://github.com/adamsutton/PyEPG'>http://github.com/adamsutton/PyEPG</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Configuration options</h2>
|
||||
<p>
|
||||
<dl>
|
||||
<dt>Module:
|
||||
<dd>Select which internal grabber to use.
|
||||
|
||||
<dt>Grab interval
|
||||
<dd>Time period between grabs.
|
||||
|
||||
<dt>External interfaces
|
||||
<dd>Check tick boxes for whichever you want to make available, the Path column displays where the unix socket you need to use lives.
|
||||
|
||||
<dt>OTA interfaces
|
||||
<dd>Check tick boxes for whichever you want to use.
|
||||
|
||||
</dl>
|
||||
Changes to any of these settings must be confirmed by pressing the
|
||||
'Save configuration' button before taking effect.
|
||||
</p>
|
||||
|
||||
<h2>Notes</h2>
|
||||
|
||||
<p>
|
||||
XMLTV/PyEPG - if you are using the internal versions of these modules then
|
||||
you must first configure them (if required) externally as TVHeadend provides
|
||||
no support for this. Once configured though TVheadend will do the rest.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Mixed grabbers - Although as much as possible is done to avoid contention, generally speaking using a mixture of grabbers should be avoided (where each grabber updates the same channels). They typically tend to contain differing information which results in them "fighting" over which information is correct and can result in a high level of EPG update messages.
|
||||
</p>
|
||||
|
||||
</div>
|
|
@ -1,33 +0,0 @@
|
|||
<div class="hts-doc-text">
|
||||
|
||||
<img src="docresources/xmltvtab.png">
|
||||
|
||||
<p>
|
||||
This tab is used to configure XML-TV. For more information about XML-TV
|
||||
and its use, please visit
|
||||
<a href="http://www.xmltv.org">http://www.xmltv.org</a>.
|
||||
|
||||
<p>
|
||||
Configuration options:
|
||||
<dl>
|
||||
<dt>XML-TV Source
|
||||
<dd>Select which grabber to use.
|
||||
When the drop down list is pressed Tvheadend will issue a scan
|
||||
for available grabbers on the host system. This result will be
|
||||
cached in the web user interface. Thus, if you need to rescan due to
|
||||
a newly installed grabber you must reload the web interface.
|
||||
(There is no need to restart Tvheadend itself).
|
||||
|
||||
When you select a grabber more information will pop up regarding
|
||||
further configuration of the grabber.
|
||||
|
||||
<dt>Grab interval
|
||||
<dd>Hours between each grab.
|
||||
|
||||
<dt>Enable grabbing
|
||||
<dd>Uncheck this if you wish to disable grabbing.
|
||||
</dl>
|
||||
Changes to any of these settings must be confirmed by pressing the
|
||||
'Save configuration' button before taking effect.
|
||||
|
||||
</div>
|
|
@ -95,10 +95,6 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
|
|||
}
|
||||
|
||||
// Note: ignore channel test if we allow quality unlocking
|
||||
// TODO: should we only apply this setting if this is actually
|
||||
/// created as a series link?
|
||||
// TODO: I could just REMOVE the channel, but I think we probably still
|
||||
// want the channel as a "preferred" option
|
||||
cfg = dvr_config_find_by_name_default(dae->dae_config_name);
|
||||
if (cfg->dvr_sl_quality_lock)
|
||||
if(dae->dae_channel != NULL &&
|
||||
|
@ -137,7 +133,7 @@ autorec_cmp(dvr_autorec_entry_t *dae, epg_broadcast_t *e)
|
|||
|
||||
// Note: dae_epnum is unset then all values are 0 and this will
|
||||
// always return 1
|
||||
epg_episode_number_full(e->episode, &epnum);
|
||||
epg_episode_get_epnum(e->episode, &epnum);
|
||||
if(epg_episode_number_cmp(&dae->dae_epnum, &epnum) < 0)
|
||||
return 0;
|
||||
|
||||
|
@ -548,7 +544,6 @@ dvr_autorec_add(const char *config_name,
|
|||
NULL, NULL, 0, NULL, creator, comment);
|
||||
}
|
||||
|
||||
/* TODO: configurable brand/series selection */
|
||||
void dvr_autorec_add_series_link
|
||||
( const char *dvr_config_name, epg_broadcast_t *event,
|
||||
const char *creator, const char *comment )
|
||||
|
@ -567,7 +562,7 @@ void dvr_autorec_add_series_link
|
|||
atime = (t.tm_hour * 60) + t.tm_min;
|
||||
}
|
||||
if (cfg->dvr_sl_more_recent) {
|
||||
epg_episode_number_full(ee, &epnum);
|
||||
epg_episode_get_epnum(ee, &epnum);
|
||||
epnump = &epnum;
|
||||
}
|
||||
_dvr_autorec_add(dvr_config_name, event->episode->title,
|
||||
|
@ -601,19 +596,15 @@ dvr_autorec_check_event(epg_broadcast_t *e)
|
|||
|
||||
void dvr_autorec_check_brand(epg_brand_t *b)
|
||||
{
|
||||
#ifdef TODO_BRAND_UPDATED_SUPPORT
|
||||
// Note: for the most part this will only be relevant should an episode
|
||||
// to which a broadcast is linked suddenly get added to a new brand
|
||||
// this is pretty damn unlikely!
|
||||
#endif
|
||||
}
|
||||
|
||||
void dvr_autorec_check_season(epg_season_t *s)
|
||||
{
|
||||
#ifdef TODO_SEASON_SUPPORT
|
||||
// Note: I guess new episodes might have been added, but again its likely
|
||||
// this will already have been picked up by the check_event call
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -369,7 +369,6 @@ dvr_entry_create_by_event(const char *config_name,
|
|||
|
||||
static int _dvr_duplicate_event ( epg_broadcast_t *e )
|
||||
{
|
||||
// TODO: include other searches?
|
||||
dvr_entry_t *de;
|
||||
LIST_FOREACH(de, &dvrentries, de_global_link) {
|
||||
if (de->de_bcast && (de->de_bcast->episode == e->episode)) return 1;
|
||||
|
@ -386,7 +385,6 @@ dvr_entry_create_by_autorec(epg_broadcast_t *e, dvr_autorec_entry_t *dae)
|
|||
char buf[200];
|
||||
|
||||
/* Dup detection */
|
||||
// TODO: need to allow overrides
|
||||
if (_dvr_duplicate_event(e)) return;
|
||||
|
||||
if(dae->dae_creator) {
|
||||
|
@ -838,7 +836,6 @@ dvr_entry_find_by_event_fuzzy(epg_broadcast_t *e)
|
|||
dvr_entry_t *
|
||||
dvr_entry_find_by_episode(epg_broadcast_t *e)
|
||||
{
|
||||
// TODO: should be configurable?
|
||||
if (e->episode) {
|
||||
dvr_entry_t *de;
|
||||
epg_broadcast_t *ebc;
|
||||
|
|
|
@ -501,12 +501,6 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
if(ch)
|
||||
addtag(q, build_tag_string("TVCHANNEL", ch->ch_name, 0, NULL));
|
||||
|
||||
#if TODO_EP_NUMBER_ONSCREEN
|
||||
if(ee && ee->onscreen)
|
||||
addtag(q, build_tag_string("SYNOPSIS",
|
||||
ee->onscreen, 0, NULL));
|
||||
#endif
|
||||
|
||||
if(de && de->de_desc)
|
||||
addtag(q, build_tag_string("SUMMARY", de->de_desc, 0, NULL));
|
||||
else if (ee && ee->description)
|
||||
|
@ -515,15 +509,20 @@ _mk_build_metadata(const dvr_entry_t *de, const epg_broadcast_t *ebc)
|
|||
addtag(q, build_tag_string("SUMMARY", ee->summary, 0, NULL));
|
||||
|
||||
if (ee) {
|
||||
if(ee->number)
|
||||
addtag(q, build_tag_int("PART_NUMBER", ee->number,
|
||||
epg_episode_num_t num;
|
||||
epg_episode_get_epnum(ee, &num);
|
||||
if(num.e_num)
|
||||
addtag(q, build_tag_int("PART_NUMBER", num.e_num,
|
||||
0, NULL));
|
||||
if(ee->season && ee->season->number)
|
||||
addtag(q, build_tag_int("PART_NUMBER", ee->season->number,
|
||||
if(num.s_num)
|
||||
addtag(q, build_tag_int("PART_NUMBER", num.s_num,
|
||||
60, "SEASON"));
|
||||
if(ee->part_number)
|
||||
addtag(q, build_tag_int("PART_NUMBER", ee->part_number,
|
||||
if(num.p_num)
|
||||
addtag(q, build_tag_int("PART_NUMBER", num.p_num,
|
||||
40, "PART"));
|
||||
if (num.text)
|
||||
addtag(q, build_tag_string("SYNOPSIS",
|
||||
num.text, 0, NULL));
|
||||
}
|
||||
|
||||
return q;
|
||||
|
|
279
src/epg.c
279
src/epg.c
|
@ -80,16 +80,23 @@ static int _season_order ( const void *_a, const void *_b )
|
|||
return a->number - b->number;
|
||||
}
|
||||
|
||||
// Note: this will do nothing with text episode numbering
|
||||
static int _episode_order ( const void *_a, const void *_b )
|
||||
{
|
||||
int r;
|
||||
int r, as, bs;
|
||||
const epg_episode_t *a = (const epg_episode_t*)_a;
|
||||
const epg_episode_t *b = (const epg_episode_t*)_b;
|
||||
r = _season_order(a->season, b->season);
|
||||
if (!a) return -1;
|
||||
if (!b) return 1;
|
||||
if (a->season) as = a->season->number;
|
||||
else as = a->epnum.s_num;
|
||||
if (b->season) bs = b->season->number;
|
||||
else bs = b->epnum.s_num;
|
||||
r = as - bs;
|
||||
if (r) return r;
|
||||
if (!a || !a->number) return 1;
|
||||
if (!b || !b->number) return -1;
|
||||
return a->number - b->number;
|
||||
r = a->epnum.e_num - b->epnum.e_num;
|
||||
if (r) return r;
|
||||
return a->epnum.p_num - b->epnum.p_num;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
|
@ -105,7 +112,7 @@ static void _epg_event_deserialize ( htsmsg_t *c, epggrab_stats_t *stats )
|
|||
uint32_t e_start = 0;
|
||||
uint32_t e_stop = 0;
|
||||
uint32_t u32;
|
||||
const char *title, *desc;
|
||||
const char *title, *desc, *str;
|
||||
char *uri;
|
||||
int save = 0;
|
||||
|
||||
|
@ -138,8 +145,10 @@ static void _epg_event_deserialize ( htsmsg_t *c, epggrab_stats_t *stats )
|
|||
save |= epg_episode_set_number(ee, u32);
|
||||
if (!htsmsg_get_u32(c, "part", &u32))
|
||||
save |= epg_episode_set_part(ee, u32, 0);
|
||||
// TODO: season number!
|
||||
// TODO: onscreen
|
||||
if (!htsmsg_get_u32(c, "season", &u32))
|
||||
ee->epnum.s_num = u32;
|
||||
if ((str = htsmsg_get_str(c, "epname")))
|
||||
ee->epnum.text = strdup(str);
|
||||
|
||||
/* Set episode */
|
||||
save |= epg_broadcast_set_episode(ebc, ee);
|
||||
|
@ -255,8 +264,6 @@ void epg_init ( void )
|
|||
memset(&stats, 0, sizeof(stats));
|
||||
while ( remain > 4 ) {
|
||||
|
||||
// TODO: would be nice if htsmsg_binary handled this for us!
|
||||
|
||||
/* Get message length */
|
||||
int msglen = (rp[0] << 24) | (rp[1] << 16) | (rp[2] << 8) | rp[3];
|
||||
remain -= 4;
|
||||
|
@ -378,8 +385,9 @@ static void _epg_object_putref ( epg_object_t *eo )
|
|||
if (!eo->refcount) eo->destroy(eo);
|
||||
}
|
||||
|
||||
static void _epg_object_set_updated ( epg_object_t *eo )
|
||||
static void _epg_object_set_updated ( void *p )
|
||||
{
|
||||
epg_object_t *eo = (epg_object_t*)p;
|
||||
if (!eo->_updated) {
|
||||
eo->_updated = 1;
|
||||
LIST_INSERT_HEAD(&epg_object_updated, eo, up_link);
|
||||
|
@ -470,6 +478,30 @@ static int _epg_object_set_str
|
|||
return save;
|
||||
}
|
||||
|
||||
static int _epg_object_set_u8
|
||||
( void *o, uint8_t *old, const uint8_t new )
|
||||
{
|
||||
int save = 0;
|
||||
if ( *old != new ) {
|
||||
*old = new;
|
||||
_epg_object_set_updated(o);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
static int _epg_object_set_u16
|
||||
( void *o, uint16_t *old, const uint16_t new )
|
||||
{
|
||||
int save = 0;
|
||||
if ( *old != new ) {
|
||||
*old = new;
|
||||
_epg_object_set_updated(o);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Brand
|
||||
* *************************************************************************/
|
||||
|
@ -792,6 +824,53 @@ epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
* Episode
|
||||
* *************************************************************************/
|
||||
|
||||
static htsmsg_t *epg_episode_num_serialize ( epg_episode_num_t *num )
|
||||
{
|
||||
htsmsg_t *m;
|
||||
if (!num) return NULL;
|
||||
m = htsmsg_create_map();
|
||||
if (num->e_num)
|
||||
htsmsg_add_u32(m, "e_num", num->e_num);
|
||||
if (num->e_cnt)
|
||||
htsmsg_add_u32(m, "e_cnt", num->e_cnt);
|
||||
if (num->s_num)
|
||||
htsmsg_add_u32(m, "s_num", num->e_num);
|
||||
if (num->s_cnt)
|
||||
htsmsg_add_u32(m, "s_cnt", num->e_cnt);
|
||||
if (num->p_num)
|
||||
htsmsg_add_u32(m, "p_num", num->e_num);
|
||||
if (num->p_cnt)
|
||||
htsmsg_add_u32(m, "p_cnt", num->e_cnt);
|
||||
if (num->text)
|
||||
htsmsg_add_str(m, "text", num->text);
|
||||
return m;
|
||||
}
|
||||
|
||||
static epg_episode_num_t *epg_episode_num_deserialize
|
||||
( htsmsg_t *m, epg_episode_num_t *num )
|
||||
{
|
||||
const char *str;
|
||||
uint32_t u32;
|
||||
if (!m) return NULL;
|
||||
if (!num) num = calloc(1, sizeof(epg_episode_num_t));
|
||||
if (!htsmsg_get_u32(m, "e_num", &u32))
|
||||
num->e_num = u32;
|
||||
if (!htsmsg_get_u32(m, "e_cnt", &u32))
|
||||
num->e_cnt = u32;
|
||||
if (!htsmsg_get_u32(m, "s_num", &u32))
|
||||
num->s_num = u32;
|
||||
if (!htsmsg_get_u32(m, "s_cnt", &u32))
|
||||
num->s_cnt = u32;
|
||||
if (!htsmsg_get_u32(m, "p_num", &u32))
|
||||
num->p_num = u32;
|
||||
if (!htsmsg_get_u32(m, "p_cnt", &u32))
|
||||
num->p_cnt = u32;
|
||||
if ((str = htsmsg_get_str(m, "text")))
|
||||
num->text = strdup(str);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static void _epg_episode_destroy ( epg_object_t *eo )
|
||||
{
|
||||
epg_episode_t *ee = (epg_episode_t*)eo;
|
||||
|
@ -814,6 +893,7 @@ static void _epg_episode_destroy ( epg_object_t *eo )
|
|||
if (ee->description) free(ee->description);
|
||||
if (ee->genre) free(ee->genre);
|
||||
if (ee->image) free(ee->image);
|
||||
if (ee->epnum.text) free(ee->epnum.text);
|
||||
free(ee);
|
||||
}
|
||||
|
||||
|
@ -876,8 +956,8 @@ int epg_episode_set_number ( epg_episode_t *episode, uint16_t number )
|
|||
{
|
||||
int save = 0;
|
||||
if ( !episode || !number ) return 0;
|
||||
if ( episode->number != number ) {
|
||||
episode->number = number;
|
||||
if ( episode->epnum.e_num != number ) {
|
||||
episode->epnum.e_num = number;
|
||||
_epg_object_set_updated((epg_object_t*)episode);
|
||||
save = 1;
|
||||
}
|
||||
|
@ -888,19 +968,40 @@ int epg_episode_set_part ( epg_episode_t *episode, uint16_t part, uint16_t count
|
|||
{
|
||||
int save = 0;
|
||||
if ( !episode || !part ) return 0;
|
||||
if ( episode->part_number != part ) {
|
||||
episode->part_number = part;
|
||||
if ( episode->epnum.p_num != part ) {
|
||||
episode->epnum.p_num = part;
|
||||
_epg_object_set_updated((epg_object_t*)episode);
|
||||
save = 1;
|
||||
}
|
||||
if ( count && episode->part_count != count ) {
|
||||
episode->part_count = count;
|
||||
if ( count && episode->epnum.p_cnt != count ) {
|
||||
episode->epnum.p_cnt = count;
|
||||
_epg_object_set_updated((epg_object_t*)episode);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
int epg_episode_set_epnum ( epg_episode_t *episode, epg_episode_num_t *num )
|
||||
{
|
||||
int save = 0;
|
||||
if (!episode || !num || (!num->e_num && !num->text)) return 0;
|
||||
if ( episode->epnum.e_num != num->e_num ) save = 1;
|
||||
else if ( episode->epnum.e_cnt != num->e_cnt ) save = 1;
|
||||
else if ( episode->epnum.s_num != num->s_num ) save = 1;
|
||||
else if ( episode->epnum.s_cnt != num->s_cnt ) save = 1;
|
||||
else if ( episode->epnum.p_num != num->p_num ) save = 1;
|
||||
else if ( episode->epnum.p_cnt != num->p_cnt ) save = 1;
|
||||
else if ( !episode->epnum.text ||
|
||||
(num->text && strcmp(num->text, episode->epnum.text)) ) save = 1;
|
||||
if (save) {
|
||||
if (episode->epnum.text) free(episode->epnum.text);
|
||||
episode->epnum = *num;
|
||||
if (episode->epnum.text) strdup(episode->epnum.text);
|
||||
save = 1;
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
int epg_episode_set_brand ( epg_episode_t *episode, epg_brand_t *brand )
|
||||
{
|
||||
int save = 0;
|
||||
|
@ -981,6 +1082,12 @@ int epg_episode_set_genre_str ( epg_episode_t *ee, const char **gstr )
|
|||
return epg_episode_set_genre(ee, genre, gcnt);
|
||||
}
|
||||
|
||||
int epg_episode_set_is_bw ( epg_episode_t *e, uint8_t bw )
|
||||
{
|
||||
if (!e) return 0;
|
||||
return _epg_object_set_u8(e, &e->is_bw, bw);
|
||||
}
|
||||
|
||||
static void _epg_episode_add_broadcast
|
||||
( epg_episode_t *episode, epg_broadcast_t *broadcast )
|
||||
{
|
||||
|
@ -1005,7 +1112,7 @@ size_t epg_episode_number_format
|
|||
size_t i = 0;
|
||||
if (!episode) return 0;
|
||||
epg_episode_num_t num;
|
||||
epg_episode_number_full(episode, &num);
|
||||
epg_episode_get_epnum(episode, &num);
|
||||
if ( num.e_num ) {
|
||||
if (pre) i += snprintf(&buf[i], len-i, "%s", pre);
|
||||
if ( sfmt && num.s_num ) {
|
||||
|
@ -1017,17 +1124,17 @@ size_t epg_episode_number_format
|
|||
i += snprintf(&buf[i], len-i, efmt, num.e_num);
|
||||
if ( cfmt && num.e_cnt )
|
||||
i+= snprintf(&buf[i], len-i, cfmt, num.e_cnt);
|
||||
} else if ( num.text ) {
|
||||
strncpy(buf, num.text, len);
|
||||
i = MAX(strlen(num.text), len);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void epg_episode_number_full ( epg_episode_t *ee, epg_episode_num_t *num )
|
||||
void epg_episode_get_epnum ( epg_episode_t *ee, epg_episode_num_t *num )
|
||||
{
|
||||
if (!ee || !num) return;
|
||||
memset(num, 0, sizeof(epg_episode_num_t));
|
||||
num->e_num = ee->number;
|
||||
num->p_num = ee->part_number;
|
||||
num->p_cnt = ee->part_count;
|
||||
*num = ee->epnum;
|
||||
if (ee->season) {
|
||||
num->e_cnt = ee->season->episode_count;
|
||||
num->s_num = ee->season->number;
|
||||
|
@ -1048,18 +1155,17 @@ int epg_episode_number_cmp ( epg_episode_num_t *a, epg_episode_num_t *b )
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// WIBNI: this could do with soem proper matching, maybe some form of
|
||||
// fuzzy string match. I did try a few things, but none of them
|
||||
// were very reliable.
|
||||
int epg_episode_fuzzy_match
|
||||
( epg_episode_t *episode, const char *uri, const char *title,
|
||||
const char *summary, const char *description )
|
||||
{
|
||||
// TODO: this is pretty noddy and likely to fail!
|
||||
// hence the reason I don't recommend mixing external grabbers and EIT
|
||||
if ( !episode ) return 0;
|
||||
if ( uri && episode->uri && !strcmp(episode->uri, uri) ) return 1;
|
||||
if ( title && episode->title && (strstr(title, episode->title) || strstr(episode->title, title)) ) return 1;
|
||||
// TODO: could we do fuzzy string matching on the description/summary
|
||||
// : there are a few algorithms that might work, but some early testing
|
||||
// : suggested it wasn't clear cut enough to make sensible decisions.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1077,16 +1183,13 @@ 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);
|
||||
if (episode->number)
|
||||
htsmsg_add_u32(m, "number", episode->number);
|
||||
if (episode->part_count && episode->part_count) {
|
||||
htsmsg_add_u32(m, "part-number", episode->part_number);
|
||||
htsmsg_add_u32(m, "part-count", episode->part_count);
|
||||
}
|
||||
htsmsg_add_msg(m, "epnum", epg_episode_num_serialize(&episode->epnum));
|
||||
if (episode->brand)
|
||||
htsmsg_add_str(m, "brand", episode->brand->uri);
|
||||
if (episode->season)
|
||||
htsmsg_add_str(m, "season", episode->season->uri);
|
||||
if (episode->is_bw)
|
||||
htsmsg_add_u32(m, "is_bw", 1);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
@ -1098,6 +1201,8 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
epg_brand_t *eb;
|
||||
uint32_t u32, u32a;
|
||||
const char *str;
|
||||
epg_episode_num_t num;
|
||||
htsmsg_t *sub;
|
||||
|
||||
if ( !_epg_object_deserialize(m, *skel) ) return NULL;
|
||||
if ( !(ee = epg_episode_find_by_uri((*skel)->uri, create, save)) ) return NULL;
|
||||
|
@ -1110,6 +1215,12 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
*save |= epg_episode_set_summary(ee, str);
|
||||
if ( (str = htsmsg_get_str(m, "description")) )
|
||||
*save |= epg_episode_set_description(ee, str);
|
||||
if ( (sub = htsmsg_get_map(m, "epnum")) ) {
|
||||
epg_episode_num_deserialize(sub, &num);
|
||||
*save |= epg_episode_set_epnum(ee, &num);
|
||||
if (num.text) free(num.text);
|
||||
}
|
||||
// Note: retained for compat
|
||||
if ( !htsmsg_get_u32(m, "number", &u32) )
|
||||
*save |= epg_episode_set_number(ee, u32);
|
||||
if ( !htsmsg_get_u32(m, "part-number", &u32) &&
|
||||
|
@ -1123,6 +1234,8 @@ epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create, int *save )
|
|||
if ( (eb = epg_brand_find_by_uri(str, 0, NULL)) )
|
||||
*save |= epg_episode_set_brand(ee, eb);
|
||||
|
||||
*save |= epg_episode_set_is_bw(ee, htsmsg_get_u32_or_default(m , "is_bw", 0));
|
||||
|
||||
return ee;
|
||||
}
|
||||
|
||||
|
@ -1313,8 +1426,8 @@ epg_broadcast_t* epg_broadcast_find_by_time
|
|||
|
||||
epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id, channel_t *ch )
|
||||
{
|
||||
// TODO: channel left in for now in case I decide to change implementation
|
||||
// to simplify the search!
|
||||
// Note: I have left channel_t param, just in case I decide to change
|
||||
// to use it for shorter search
|
||||
return (epg_broadcast_t*)_epg_object_find_by_id(id);
|
||||
}
|
||||
|
||||
|
@ -1346,6 +1459,60 @@ int epg_broadcast_set_episode
|
|||
return save;
|
||||
}
|
||||
|
||||
int epg_broadcast_set_is_widescreen ( epg_broadcast_t *b, uint8_t ws )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u8(b, &b->is_widescreen, ws);
|
||||
}
|
||||
|
||||
int epg_broadcast_set_is_hd ( epg_broadcast_t *b, uint8_t hd )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u8(b, &b->is_hd, hd);
|
||||
}
|
||||
|
||||
int epg_broadcast_set_lines ( epg_broadcast_t *b, uint16_t lines )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u16(b, &b->lines, lines);
|
||||
}
|
||||
|
||||
int epg_broadcast_set_aspect ( epg_broadcast_t *b, uint16_t aspect )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u16(b, &b->aspect, aspect);
|
||||
}
|
||||
|
||||
int epg_broadcast_set_is_deafsigned ( epg_broadcast_t *b, uint8_t ds )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u8(b, &b->is_deafsigned, ds);
|
||||
}
|
||||
|
||||
int epg_broadcast_set_is_subtitled ( epg_broadcast_t *b, uint8_t st )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u8(b, &b->is_subtitled, st);
|
||||
}
|
||||
|
||||
int epg_broadcast_set_is_audio_desc ( epg_broadcast_t *b, uint8_t ad )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u8(b, &b->is_audio_desc, ad);
|
||||
}
|
||||
|
||||
int epg_broadcast_set_is_new ( epg_broadcast_t *b, uint8_t n )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u8(b, &b->is_new, n);
|
||||
}
|
||||
|
||||
int epg_broadcast_set_is_repeat ( epg_broadcast_t *b, uint8_t r )
|
||||
{
|
||||
if (!b) return 0;
|
||||
return _epg_object_set_u8(b, &b->is_repeat, r);
|
||||
}
|
||||
|
||||
epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *broadcast )
|
||||
{
|
||||
if ( !broadcast ) return NULL;
|
||||
|
@ -1365,6 +1532,24 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
|
|||
htsmsg_add_u32(m, "channel", broadcast->channel->ch_id);
|
||||
if (broadcast->dvb_eid)
|
||||
htsmsg_add_u32(m, "dvb_eid", broadcast->dvb_eid);
|
||||
if (broadcast->is_widescreen)
|
||||
htsmsg_add_u32(m, "is_widescreen", 1);
|
||||
if (broadcast->is_hd)
|
||||
htsmsg_add_u32(m, "is_hd", 1);
|
||||
if (broadcast->lines)
|
||||
htsmsg_add_u32(m, "lines", broadcast->lines);
|
||||
if (broadcast->aspect)
|
||||
htsmsg_add_u32(m, "aspect", broadcast->aspect);
|
||||
if (broadcast->is_deafsigned)
|
||||
htsmsg_add_u32(m, "is_deafsigned", 1);
|
||||
if (broadcast->is_subtitled)
|
||||
htsmsg_add_u32(m, "is_subtitled", 1);
|
||||
if (broadcast->is_audio_desc)
|
||||
htsmsg_add_u32(m, "is_audio_desc", 1);
|
||||
if (broadcast->is_new)
|
||||
htsmsg_add_u32(m, "is_new", 1);
|
||||
if (broadcast->is_repeat)
|
||||
htsmsg_add_u32(m, "is_repeat", 1);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
@ -1376,7 +1561,7 @@ epg_broadcast_t *epg_broadcast_deserialize
|
|||
epg_broadcast_t *ret, **ebc = _epg_broadcast_skel();
|
||||
epg_episode_t *ee;
|
||||
const char *str;
|
||||
uint32_t chid, eid, start, stop;
|
||||
uint32_t chid, eid, start, stop, u32;
|
||||
|
||||
if ( htsmsg_get_u32(m, "start", &start) ) return NULL;
|
||||
if ( htsmsg_get_u32(m, "stop", &stop) ) return NULL;
|
||||
|
@ -1401,6 +1586,26 @@ epg_broadcast_t *epg_broadcast_deserialize
|
|||
ch = channel_find_by_identifier(chid);
|
||||
}
|
||||
|
||||
/* Get metadata */
|
||||
if (!htsmsg_get_u32(m, "is_widescreen", &u32))
|
||||
(*ebc)->is_widescreen = 1;
|
||||
if (!htsmsg_get_u32(m, "is_hd", &u32))
|
||||
(*ebc)->is_hd = 1;
|
||||
if (!htsmsg_get_u32(m, "lines", &u32))
|
||||
(*ebc)->lines = u32;
|
||||
if (!htsmsg_get_u32(m, "aspect", &u32))
|
||||
(*ebc)->aspect = u32;
|
||||
if (!htsmsg_get_u32(m, "is_deafsigned", &u32))
|
||||
(*ebc)->is_deafsigned = 1;
|
||||
if (!htsmsg_get_u32(m, "is_subtitled", &u32))
|
||||
(*ebc)->is_subtitled = 1;
|
||||
if (!htsmsg_get_u32(m, "is_audio_desc", &u32))
|
||||
(*ebc)->is_audio_desc = 1;
|
||||
if (!htsmsg_get_u32(m, "is_new", &u32))
|
||||
(*ebc)->is_new = 1;
|
||||
if (!htsmsg_get_u32(m, "is_repeat", &u32))
|
||||
(*ebc)->is_repeat = 1;
|
||||
|
||||
/* Add to channel */
|
||||
if ( ch ) {
|
||||
ret = _epg_channel_add_broadcast(ch, ebc, create, save);
|
||||
|
|
53
src/epg.h
53
src/epg.h
|
@ -181,12 +181,13 @@ epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create, int *save );
|
|||
*/
|
||||
typedef struct epg_episode_num
|
||||
{
|
||||
uint16_t s_num; ///< Series number
|
||||
uint16_t s_cnt; ///< Series count
|
||||
uint16_t e_num; ///< Episode number
|
||||
uint16_t e_cnt; ///< Episode count
|
||||
uint16_t p_num; ///< Part number
|
||||
uint16_t p_cnt; ///< Part count
|
||||
uint16_t s_num; ///< Series number
|
||||
uint16_t s_cnt; ///< Series count
|
||||
uint16_t e_num; ///< Episode number
|
||||
uint16_t e_cnt; ///< Episode count
|
||||
uint16_t p_num; ///< Part number
|
||||
uint16_t p_cnt; ///< Part count
|
||||
char *text; ///< Arbitary text description of episode num
|
||||
} epg_episode_num_t;
|
||||
|
||||
/* Object */
|
||||
|
@ -200,11 +201,14 @@ struct epg_episode
|
|||
char *description; ///< An extended description
|
||||
uint8_t *genre; ///< Episode genre(s)
|
||||
int genre_cnt; ///< Genre count
|
||||
uint16_t number; ///< The episode number
|
||||
uint16_t part_number; ///< For multipart episodes
|
||||
uint16_t part_count; ///< For multipart episodes
|
||||
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
|
||||
// TODO: film/year
|
||||
|
||||
LIST_ENTRY(epg_episode) blink; ///< Brand link
|
||||
LIST_ENTRY(epg_episode) slink; ///< Season link
|
||||
epg_brand_t *brand; ///< (Grand-)Parent brand
|
||||
|
@ -229,11 +233,11 @@ int epg_episode_set_description ( epg_episode_t *e, const char *description )
|
|||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_number ( epg_episode_t *e, uint16_t number )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_onscreen ( epg_episode_t *e, const char *onscreen )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_part ( epg_episode_t *e,
|
||||
uint16_t number, uint16_t count )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_epnum ( epg_episode_t *e, epg_episode_num_t *num )
|
||||
__attribute__((warn_unused_result));
|
||||
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 )
|
||||
|
@ -244,7 +248,12 @@ int epg_episode_set_genre_str ( epg_episode_t *e, const char **s )
|
|||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_image ( epg_episode_t *e, const char *i )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_episode_set_is_bw ( epg_episode_t *b, uint8_t bw )
|
||||
__attribute__((warn_unused_result));
|
||||
|
||||
// Note: this does NOT strdup the text field
|
||||
void epg_episode_get_epnum
|
||||
( epg_episode_t *e, epg_episode_num_t *epnum );
|
||||
/* EpNum format helper */
|
||||
// output string will be:
|
||||
// if (episode_num)
|
||||
|
@ -260,8 +269,6 @@ size_t epg_episode_number_format
|
|||
const char *pre, const char *sfmt,
|
||||
const char *sep, const char *efmt,
|
||||
const char *cfmt );
|
||||
void epg_episode_number_full
|
||||
( epg_episode_t *e, epg_episode_num_t *epnum );
|
||||
int epg_episode_number_cmp
|
||||
( epg_episode_num_t *a, epg_episode_num_t *b );
|
||||
|
||||
|
@ -318,6 +325,24 @@ epg_broadcast_t *epg_broadcast_find_by_eid ( int eid, struct channel *ch );
|
|||
/* Mutators */
|
||||
int epg_broadcast_set_episode ( epg_broadcast_t *b, epg_episode_t *e )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_is_widescreen ( epg_broadcast_t *b, uint8_t ws )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_is_hd ( epg_broadcast_t *b, uint8_t hd )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_lines ( epg_broadcast_t *b, uint16_t lines )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_aspect ( epg_broadcast_t *b, uint16_t aspect )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_is_deafsigned ( epg_broadcast_t *b, uint8_t ds )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_is_subtitled ( epg_broadcast_t *b, uint8_t st )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_is_audio_desc ( epg_broadcast_t *b, uint8_t ad )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_is_new ( epg_broadcast_t *b, uint8_t n )
|
||||
__attribute__((warn_unused_result));
|
||||
int epg_broadcast_set_is_repeat ( epg_broadcast_t *b, uint8_t r )
|
||||
__attribute__((warn_unused_result));
|
||||
|
||||
/* Accessors */
|
||||
epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *b );
|
||||
|
@ -361,7 +386,7 @@ typedef struct epg_query_result {
|
|||
void epg_query_free(epg_query_result_t *eqr);
|
||||
|
||||
/* Sorting */
|
||||
// TODO: comparator function input?
|
||||
// WIBNI: might be useful to have a user defined comparator function
|
||||
void epg_query_sort(epg_query_result_t *eqr);
|
||||
|
||||
/* Query routines */
|
||||
|
|
|
@ -100,8 +100,6 @@ static void _epggrab_module_grab ( epggrab_module_t *mod )
|
|||
|
||||
/*
|
||||
* Socket handler
|
||||
*
|
||||
* TODO: could make this threaded to allow multiple simultaneous inputs
|
||||
*/
|
||||
static void _epggrab_socket_handler ( epggrab_module_t *mod, int s )
|
||||
{
|
||||
|
@ -170,10 +168,6 @@ static void* _epggrab_internal_thread ( void* p )
|
|||
|
||||
/*
|
||||
* External (socket) grab thread
|
||||
*
|
||||
* TODO: I could common all of this up and have a single thread
|
||||
* servicing all the available sockets, but we're unlikely to
|
||||
* have a massive number of modules enabled anyway!
|
||||
*/
|
||||
static void *_epggrab_socket_thread ( void *p )
|
||||
{
|
||||
|
@ -202,7 +196,6 @@ static int _ch_id_cmp ( void *a, void *b )
|
|||
((epggrab_channel_t*)b)->id);
|
||||
}
|
||||
|
||||
// TODO: add other matches
|
||||
static int _ch_link ( epggrab_channel_t *ec, channel_t *ch )
|
||||
{
|
||||
service_t *sv;
|
||||
|
@ -249,7 +242,6 @@ static int _ch_link ( epggrab_channel_t *ec, channel_t *ch )
|
|||
return match;
|
||||
}
|
||||
|
||||
// TODO: could use TCP socket to allow remote access
|
||||
int epggrab_module_enable_socket ( epggrab_module_t *mod, uint8_t e )
|
||||
{
|
||||
pthread_t tid;
|
||||
|
@ -343,7 +335,6 @@ htsmsg_t *epggrab_module_trans_xml
|
|||
return ret;
|
||||
}
|
||||
|
||||
// TODO: add extra metadata
|
||||
void epggrab_module_channel_save
|
||||
( epggrab_module_t *mod, epggrab_channel_t *ch )
|
||||
{
|
||||
|
@ -373,6 +364,8 @@ void epggrab_module_channel_save
|
|||
}
|
||||
htsmsg_add_msg(m, "sname", a);
|
||||
}
|
||||
if (ch->number)
|
||||
htsmsg_add_u32(m, "number", ch->number);
|
||||
|
||||
hts_settings_save(m, "epggrab/%s/channels/%s", mod->id, ch->id);
|
||||
}
|
||||
|
@ -413,6 +406,8 @@ static void epggrab_module_channel_load
|
|||
i++;
|
||||
}
|
||||
}
|
||||
if(!htsmsg_get_u32(m, "number", &u32))
|
||||
ch->number = u32;
|
||||
|
||||
if (!htsmsg_get_u32(m, "channel", &u32))
|
||||
ch->channel = channel_find_by_identifier(u32);
|
||||
|
@ -508,7 +503,6 @@ int epggrab_channel_set_name ( epggrab_channel_t *ec, const char *name )
|
|||
return save;
|
||||
}
|
||||
|
||||
// TODO: what a mess!
|
||||
int epggrab_channel_set_sid
|
||||
( epggrab_channel_t *ec, const uint16_t *sid, int num )
|
||||
{
|
||||
|
@ -535,7 +529,6 @@ int epggrab_channel_set_sid
|
|||
return save;
|
||||
}
|
||||
|
||||
// TODO: what a mess!
|
||||
int epggrab_channel_set_sname ( epggrab_channel_t *ec, const char **sname )
|
||||
{
|
||||
int save = 0, i = 0;
|
||||
|
@ -595,9 +588,6 @@ int epggrab_channel_set_number ( epggrab_channel_t *ec, int number )
|
|||
return save;
|
||||
}
|
||||
|
||||
// TODO: add other match critera
|
||||
// TODO: add additional metadata updates
|
||||
// TODO: add configurable updating
|
||||
void epggrab_channel_link ( epggrab_channel_t *ec )
|
||||
{
|
||||
channel_t *ch;
|
||||
|
@ -618,8 +608,6 @@ void epggrab_channel_updated ( epggrab_channel_t *ec )
|
|||
epggrab_module_channel_save(ec->mod, ec);
|
||||
}
|
||||
|
||||
// TODO: currently lists ALL channels from ALL active modules
|
||||
// TODO: won't work if channels are handled internally within module!
|
||||
htsmsg_t *epggrab_channel_list ( void )
|
||||
{
|
||||
char name[100];
|
||||
|
|
|
@ -48,7 +48,6 @@ typedef struct epggrab_channel
|
|||
char *icon; ///< Channel icon
|
||||
int number; ///< Channel number
|
||||
|
||||
// TODO: I think we might need a list of channels!
|
||||
struct channel *channel; ///< Mapped channel
|
||||
} epggrab_channel_t;
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "spawn.h"
|
||||
#include "epg.h"
|
||||
#include "epggrab/pyepg.h"
|
||||
#include "epggrab/xmltv.h"
|
||||
#include "channels.h"
|
||||
|
||||
static epggrab_channel_tree_t _pyepg_channels;
|
||||
|
@ -293,7 +294,9 @@ static int _pyepg_parse_episode ( htsmsg_t *data, epggrab_stats_t *stats )
|
|||
save |= epg_episode_set_genre(episode, genre, genre_cnt);
|
||||
}
|
||||
|
||||
/* TODO: extra metadata */
|
||||
/* Content */
|
||||
if ((htsmsg_get_map(tags, "blackandwhite")))
|
||||
save |= epg_episode_set_is_bw(episode, 1);
|
||||
|
||||
if (save) stats->episodes.modified++;
|
||||
|
||||
|
@ -304,11 +307,12 @@ static int _pyepg_parse_broadcast
|
|||
( htsmsg_t *data, channel_t *channel, epggrab_stats_t *stats )
|
||||
{
|
||||
int save = 0;
|
||||
htsmsg_t *attr;//, *tags;
|
||||
htsmsg_t *attr, *tags;
|
||||
epg_episode_t *episode;
|
||||
epg_broadcast_t *broadcast;
|
||||
const char *id, *start, *stop;
|
||||
time_t tm_start, tm_stop;
|
||||
uint32_t u32;
|
||||
|
||||
if ( data == NULL || channel == NULL ) return 0;
|
||||
|
||||
|
@ -316,6 +320,7 @@ static int _pyepg_parse_broadcast
|
|||
if ((id = htsmsg_get_str(attr, "episode")) == NULL) return 0;
|
||||
if ((start = htsmsg_get_str(attr, "start")) == NULL ) return 0;
|
||||
if ((stop = htsmsg_get_str(attr, "stop")) == NULL ) return 0;
|
||||
if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0;
|
||||
|
||||
/* Find episode */
|
||||
if ((episode = epg_episode_find_by_uri(id, 1, &save)) == NULL) return 0;
|
||||
|
@ -333,8 +338,23 @@ static int _pyepg_parse_broadcast
|
|||
/* Set episode */
|
||||
save |= epg_broadcast_set_episode(broadcast, episode);
|
||||
|
||||
/* TODO: extra metadata */
|
||||
|
||||
/* Quality */
|
||||
u32 = htsmsg_get_map(tags, "hd") ? 1 : 0;
|
||||
save |= epg_broadcast_set_is_hd(broadcast, u32);
|
||||
u32 = htsmsg_get_map(tags, "widescreen") ? 1 : 0;
|
||||
save |= epg_broadcast_set_is_widescreen(broadcast, u32);
|
||||
// TODO: lines, aspect
|
||||
|
||||
/* Accessibility */
|
||||
// Note: reuse XMLTV parse code as this is the same
|
||||
xmltv_parse_accessibility(broadcast, tags);
|
||||
|
||||
/* New/Repeat */
|
||||
u32 = htsmsg_get_map(tags, "new") || htsmsg_get_map(tags, "premiere");
|
||||
save |= epg_broadcast_set_is_new(broadcast, u32);
|
||||
u32 = htsmsg_get_map(tags, "repeat") ? 1 : 0;
|
||||
save |= epg_broadcast_set_is_repeat(broadcast, u32);
|
||||
|
||||
if (save) stats->broadcasts.modified++;
|
||||
|
||||
return save;
|
||||
|
|
|
@ -204,6 +204,85 @@ get_episode_info
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process video quality flags
|
||||
*
|
||||
* Note: this is very rough/approx someone might be able to do a much better
|
||||
* job
|
||||
*/
|
||||
static int
|
||||
parse_vid_quality ( epg_broadcast_t *ebc, epg_episode_t *ee, htsmsg_t *m )
|
||||
{
|
||||
int save = 0;
|
||||
int hd = 0, lines = 0, aspect = 0;
|
||||
const char *str;
|
||||
if (!ebc || !m) return 0;
|
||||
|
||||
if ((str = htsmsg_xml_get_cdata_str(m, "colour")))
|
||||
save |= epg_episode_set_is_bw(ee, strcmp(str, "no") ? 0 : 1);
|
||||
if ((str = htsmsg_xml_get_cdata_str(m, "quality"))) {
|
||||
if (strstr(str, "HD")) {
|
||||
hd = 1;
|
||||
} else if (strstr(str, "480")) {
|
||||
lines = 480;
|
||||
aspect = 150;
|
||||
} else if (strstr(str, "576")) {
|
||||
lines = 576;
|
||||
aspect = 133;
|
||||
} else if (strstr(str, "720")) {
|
||||
lines = 720;
|
||||
hd = 1;
|
||||
aspect = 178;
|
||||
} else if (strstr(str, "1080")) {
|
||||
lines = 1080;
|
||||
hd = 1;
|
||||
aspect = 178;
|
||||
}
|
||||
}
|
||||
if ((str = htsmsg_xml_get_cdata_str(m, "aspect"))) {
|
||||
int w, h;
|
||||
if (sscanf(str, "%d:%d", &w, &h) == 2) {
|
||||
aspect = (100 * w) / h;
|
||||
}
|
||||
}
|
||||
save |= epg_broadcast_set_is_hd(ebc, hd);
|
||||
if (aspect) {
|
||||
save |= epg_broadcast_set_is_widescreen(ebc, hd || aspect > 137);
|
||||
save |= epg_broadcast_set_aspect(ebc, aspect);
|
||||
}
|
||||
if (lines)
|
||||
save |= epg_broadcast_set_lines(ebc, lines);
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse accessibility data
|
||||
*/
|
||||
int
|
||||
xmltv_parse_accessibility ( epg_broadcast_t *ebc, htsmsg_t *m )
|
||||
{
|
||||
int save = 0;
|
||||
htsmsg_t *tag;
|
||||
htsmsg_field_t *f;
|
||||
const char *str;
|
||||
|
||||
HTSMSG_FOREACH(f, m) {
|
||||
if(!strcmp(f->hmf_name, "subtitles")) {
|
||||
if ((tag = htsmsg_get_map_by_field(f))) {
|
||||
str = htsmsg_xml_get_attr_str(tag, "type");
|
||||
if (str && !strcmp(str, "teletext"))
|
||||
save |= epg_broadcast_set_is_subtitled(ebc, 1);
|
||||
else if (str && !strcmp(str, "deaf-signed"))
|
||||
save |= epg_broadcast_set_is_deafsigned(ebc, 1);
|
||||
}
|
||||
} else if (!strcmp(f->hmf_name, "audio-described")) {
|
||||
save |= epg_broadcast_set_is_audio_desc(ebc, 1);
|
||||
}
|
||||
}
|
||||
return save;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse tags inside of a programme
|
||||
*/
|
||||
|
@ -242,16 +321,32 @@ _xmltv_parse_programme_tags(channel_t *ch, htsmsg_t *tags,
|
|||
if (en) save |= epg_episode_set_number(ee, en);
|
||||
if (save) stats->episodes.modified++;
|
||||
|
||||
// TODO: need to handle certification and ratings
|
||||
// TODO: need to handle season numbering!
|
||||
// TODO: need to handle onscreen numbering
|
||||
//if (onscreen) save |= epg_episode_set_onscreen(ee, onscreen);
|
||||
|
||||
/* Create/Find broadcast */
|
||||
ebc = epg_broadcast_find_by_time(ch, start, stop, 1, &save2);
|
||||
if ( ebc != NULL ) {
|
||||
if ( ebc ) {
|
||||
stats->broadcasts.total++;
|
||||
if (save2) stats->broadcasts.created++;
|
||||
save2 |= epg_broadcast_set_episode(ebc, ee);
|
||||
|
||||
/* Quality metadata */
|
||||
save2 |= parse_vid_quality(ebc, ee, htsmsg_get_map(tags, "video"));
|
||||
|
||||
/* Accessibility */
|
||||
save2 |= xmltv_parse_accessibility(ebc, tags);
|
||||
|
||||
/* Misc */
|
||||
if (htsmsg_get_map(tags, "previously-shown"))
|
||||
save |= epg_broadcast_set_is_repeat(ebc, 1);
|
||||
else if (htsmsg_get_map(tags, "premiere") ||
|
||||
htsmsg_get_map(tags, "new"))
|
||||
save |= epg_broadcast_set_is_new(ebc, 1);
|
||||
|
||||
/* Stats */
|
||||
if (save2) stats->broadcasts.modified++;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,4 +25,7 @@
|
|||
void xmltv_init ( epggrab_module_list_t *list );
|
||||
void xmltv_load ( void );
|
||||
|
||||
// reused by pyepg
|
||||
int xmltv_parse_accessibility ( epg_broadcast_t *ebc, htsmsg_t *m );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
tvheadend.brands = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
// TODO: this is not ALL fields, just those that I'm likely to use here
|
||||
fields: [ 'uri', 'title' ],
|
||||
autoLoad: true,
|
||||
url : 'epgobject',
|
||||
baseParams : { op : 'brandList' }
|
||||
});
|
||||
// TODO: we might want this to periodically update!
|
||||
// WIBNI: might want this store to periodically update
|
||||
|
||||
tvheadend.ContentGroupStore = new Ext.data.JsonStore({
|
||||
root:'entries',
|
||||
|
@ -117,7 +116,6 @@ tvheadend.epgDetails = function(event) {
|
|||
}
|
||||
|
||||
function showAlternatives (s) {
|
||||
// TODO: must be a way to constrain this
|
||||
var e = Ext.get('altbcast')
|
||||
html = '';
|
||||
if ( s.getTotalCount() > 0 ) {
|
||||
|
@ -126,14 +124,12 @@ tvheadend.epgDetails = function(event) {
|
|||
var ab = s.getAt(i).data;
|
||||
var dt = Date.parseDate(ab.start, 'U');
|
||||
html += '<div class="x-epg-desc">' + dt.format('l H:i') + ' ' + ab.channel + '</div>';
|
||||
// TODO: record option?
|
||||
}
|
||||
}
|
||||
e.dom.innerHTML = html;
|
||||
}
|
||||
function showRelated (s)
|
||||
{
|
||||
// TODO: must be a way to constrain this
|
||||
var e = Ext.get('related')
|
||||
html = '';
|
||||
if ( s.getTotalCount() > 0 ) {
|
||||
|
|
|
@ -176,7 +176,6 @@ tvheadend.epggrab = function() {
|
|||
dataIndex : 'path',
|
||||
width : 300,
|
||||
sortable : false,
|
||||
// TODO: editable?
|
||||
},
|
||||
]);
|
||||
|
||||
|
@ -258,7 +257,8 @@ tvheadend.epggrab = function() {
|
|||
var helpButton = new Ext.Button({
|
||||
text : 'Help',
|
||||
handler : function() {
|
||||
alert('TODO: help info');
|
||||
new tvheadend.help('EPG Grab Configuration',
|
||||
'config_epggrab.html');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue