Started work on migrating EPG schedules into the main channel_t structure.

This commit is contained in:
Adam Sutton 2012-05-30 14:40:04 +01:00
parent a373a3b4d8
commit 74cfefd965
9 changed files with 298 additions and 323 deletions

345
src/epg.c
View file

@ -16,16 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* TODO: list:
*
* - sanity checks on _destroy calls could just do what we're told and
* unreference all links?
*
* - does the broadcast <-> channel referencing need to be 2 way?
* i.e. do we need to hold onto the CHANNEL info for DVR held broadcasts?
*/
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdio.h>
@ -54,13 +44,6 @@ epg_object_list_t epg_objects[EPG_HASH_WIDTH];
epg_object_tree_t epg_brands;
epg_object_tree_t epg_seasons;
epg_object_tree_t epg_episodes;
epg_object_tree_t epg_channels;
/* Unmapped */
LIST_HEAD(epg_channel_unmapped_list, epg_channel);
LIST_HEAD(channel_unmapped_list, channel);
struct epg_channel_unmapped_list epg_channel_unmapped;
struct channel_unmapped_list channel_unmapped;
/* Unreferenced */
epg_object_list_t epg_object_unref;
@ -89,13 +72,6 @@ static int _ebc_win_cmp ( const void *a, const void *b )
return 0;
}
static int _epg_channel_cmp ( epg_channel_t *ec, channel_t *ch )
{
int ret = 0;
if ( ec->name && !strcmp(ec->name, ch->ch_name) ) ret = 1;
return ret;
}
/* **************************************************************************
* Setup / Update
* *************************************************************************/
@ -113,6 +89,8 @@ static int _epg_write ( int fd, htsmsg_t *m )
free(msgdata);
if(w == msglen) ret = 0;
}
} else {
ret = 0;
}
if(ret) {
tvhlog(LOG_DEBUG, "epg", "failed to store epg to disk");
@ -132,18 +110,13 @@ static int _epg_write_sect ( int fd, const char *sect )
void epg_save ( void )
{
int fd;
epg_object_t *eo, *ec;
epg_object_t *eo;
channel_t *ch;
epggrab_stats_t stats;
fd = hts_settings_open_file(1, "epgdb");
/* Channels */
memset(&stats, 0, sizeof(stats));
if ( _epg_write_sect(fd, "channels") ) return;
RB_FOREACH(eo, &epg_channels, glink) {
if (_epg_write(fd, epg_channel_serialize((epg_channel_t*)eo))) return;
stats.channels.total++;
}
if ( _epg_write_sect(fd, "brands") ) return;
RB_FOREACH(eo, &epg_brands, glink) {
if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) return;
@ -160,8 +133,8 @@ void epg_save ( void )
stats.episodes.total++;
}
if ( _epg_write_sect(fd, "broadcasts") ) return;
RB_FOREACH(ec, &epg_channels, glink) {
RB_FOREACH(eo, &((epg_channel_t*)ec)->schedule, glink) {
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
RB_FOREACH(eo, &ch->ch_epg_schedule, glink) {
if (_epg_write(fd, epg_broadcast_serialize((epg_broadcast_t*)eo))) return;
stats.broadcasts.total++;
}
@ -169,7 +142,6 @@ void epg_save ( void )
/* Stats */
tvhlog(LOG_DEBUG, "epg", "database saved");
tvhlog(LOG_DEBUG, "epg", " channels %d", stats.channels.total);
tvhlog(LOG_DEBUG, "epg", " brands %d", stats.brands.total);
tvhlog(LOG_DEBUG, "epg", " seasons %d", stats.seasons.total);
tvhlog(LOG_DEBUG, "epg", " episodes %d", stats.episodes.total);
@ -230,10 +202,6 @@ void epg_init ( void )
if (sect) free(sect);
sect = strdup(s);
/* Channel */
} else if ( !strcmp(sect, "channels") ) {
if (epg_channel_deserialize(m, 1, &save)) stats.channels.total++;
/* Brand */
} else if ( !strcmp(sect, "brands") ) {
if (epg_brand_deserialize(m, 1, &save)) stats.brands.total++;
@ -297,8 +265,10 @@ void epg_updated ( void )
static void _epg_object_destroy ( epg_object_t *eo, epg_object_tree_t *tree )
{
assert(eo->refcount == 0);
if (eo->uri) free(eo->uri);
if (tree) RB_REMOVE(tree, eo, glink);
LIST_REMOVE(eo, hlink);
}
static void _epg_object_getref ( epg_object_t *eo )
@ -335,7 +305,7 @@ static epg_object_t *_epg_object_find
if (!eo->putref) eo->putref = _epg_object_putref;
_epg_object_idx++;
LIST_INSERT_HEAD(&epg_object_unref, eo, ulink);
LIST_INSERT_HEAD(&epg_objects[eo->id & EPG_HASH_WIDTH], eo, hlink);
LIST_INSERT_HEAD(&epg_objects[eo->id & EPG_HASH_MASK], eo, hlink);
}
}
@ -366,7 +336,7 @@ static epg_object_t *_epg_object_find_by_uri
static epg_object_t *_epg_object_find_by_id2 ( uint64_t id )
{
epg_object_t *eo;
LIST_FOREACH(eo, &epg_objects[id & EPG_HASH_WIDTH], hlink) {
LIST_FOREACH(eo, &epg_objects[id & EPG_HASH_MASK], hlink) {
if (eo->id == id) return eo;
}
return NULL;
@ -410,6 +380,7 @@ epg_brand_t* epg_brand_find_by_uri
static epg_object_t *skel = NULL;
if ( !skel ) {
skel = calloc(1, sizeof(epg_brand_t));
skel->type = EPG_BRAND;
skel->destroy = _epg_brand_destroy;
}
return (epg_brand_t*)
@ -543,6 +514,7 @@ epg_season_t* epg_season_find_by_uri
static epg_object_t *skel = NULL;
if ( !skel ) {
skel = calloc(1, sizeof(epg_season_t));
skel->type = EPG_SEASON;
skel->destroy = _epg_season_destroy;
}
return (epg_season_t*)
@ -694,6 +666,7 @@ epg_episode_t* epg_episode_find_by_uri
static epg_object_t *skel = NULL;
if ( !skel ) {
skel = calloc(1, sizeof(epg_episode_t));
skel->type = EPG_EPISODE;
skel->destroy = _epg_episode_destroy;
}
return (epg_episode_t*)
@ -939,17 +912,17 @@ static void _epg_broadcast_destroy ( epg_object_t *eo )
}
epg_broadcast_t* epg_broadcast_find_by_time
( epg_channel_t *channel, time_t start, time_t stop, int create, int *save )
( channel_t *channel, time_t start, time_t stop, int create, int *save )
{
return epg_channel_get_broadcast(channel, start, stop, create, save);
}
// TODO: allow optional channel parameter?
epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id, epg_channel_t *ec )
epg_broadcast_t *epg_broadcast_find_by_id ( uint64_t id, channel_t *ch )
{
epg_object_t *eo = NULL;
if ( ec ) {
eo = _epg_object_find_by_id(id, &((epg_channel_t*)ec)->schedule);
if ( ch ) {
eo = _epg_object_find_by_id(id, &ch->ch_epg_schedule);
} else {
eo = _epg_object_find_by_id2(id);
}
@ -984,31 +957,27 @@ htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
{
htsmsg_t *m;
if (!broadcast) return NULL;
if (!broadcast->channel || !broadcast->channel->_.uri) return NULL;
if (!broadcast->episode || !broadcast->episode->_.uri) return NULL;
m = htsmsg_create_map();
htsmsg_add_u32(m, "id", broadcast->_.id);
htsmsg_add_u64(m, "id", broadcast->_.id);
htsmsg_add_u32(m, "start", broadcast->start);
htsmsg_add_u32(m, "stop", broadcast->stop);
if (broadcast->channel)
htsmsg_add_str(m, "channel", broadcast->channel->_.uri);
htsmsg_add_u32(m, "channel", broadcast->channel->ch_id);
htsmsg_add_str(m, "episode", broadcast->episode->_.uri);
if (broadcast->dvb_id)
htsmsg_add_u32(m, "dvb-id", broadcast->dvb_id);
// TODO: add other metadata fields
return m;
}
epg_broadcast_t *epg_broadcast_deserialize
( htsmsg_t *m, int create, int *save )
{
channel_t *ch;
epg_broadcast_t *ebc;
epg_channel_t *ec;
epg_episode_t *ee;
const char *str;
uint32_t start, stop;
uint32_t chid, start, stop;
uint64_t id;
// TODO: need to handle broadcasts without a channel
@ -1018,12 +987,12 @@ epg_broadcast_t *epg_broadcast_deserialize
if ( htsmsg_get_u64(m, "id", &id) ) return NULL;
if ( htsmsg_get_u32(m, "start", &start) ) return NULL;
if ( htsmsg_get_u32(m, "stop", &stop) ) return NULL;
if ( !(str = htsmsg_get_str(m, "channel")) ) return NULL;
if ( !(ec = epg_channel_find_by_uri(str, 0, NULL)) ) return NULL;
if ( htsmsg_get_u32(m, "channel", &chid) ) return NULL;
if ( !(ch = channel_find_by_identifier(chid)) ) return NULL;
if ( !(str = htsmsg_get_str(m, "episode")) ) return NULL;
if ( !(ee = epg_episode_find_by_uri(str, 0, NULL)) ) return NULL;
ebc = epg_broadcast_find_by_time(ec, start, stop, create, save);
ebc = epg_broadcast_find_by_time(ch, start, stop, create, save);
if ( !ebc ) return NULL;
*save |= epg_broadcast_set_episode(ebc, ee);
@ -1032,11 +1001,7 @@ epg_broadcast_t *epg_broadcast_deserialize
ebc->_.id = id;
if ( id >= _epg_object_idx ) _epg_object_idx = id + 1;
#if TODO_BROADCAST_METADATA
if ( !htsmsg_get_u32(m, "dvb-id", &u32) )
save |= epg_broadcast_set_dvb_id(ebc, u32);
// TODO: more metadata
#endif
return ebc;
}
@ -1050,281 +1015,101 @@ static void _epg_channel_timer_callback ( void *p )
time_t next = 0;
epg_object_t *eo;
epg_broadcast_t *ebc, *cur;
epg_channel_t *ec = (epg_channel_t*)p;
channel_t *ch = (channel_t*)p;
/* Clear now/next */
cur = ec->now;
ec->now = ec->next = NULL;
cur = ch->ch_epg_now;
ch->ch_epg_now = ch->ch_epg_next = NULL;
/* Check events */
while ( (eo = RB_FIRST(&ec->schedule)) ) {
while ( (eo = RB_FIRST(&ch->ch_epg_schedule)) ) {
ebc = (epg_broadcast_t*)eo;
/* Expire */
if ( ebc->stop <= dispatch_clock ) {
RB_REMOVE(&ec->schedule, eo, glink);
RB_REMOVE(&ch->ch_epg_schedule, eo, glink);
tvhlog(LOG_DEBUG, "epg", "expire event %lu from %s",
eo->id, ec->_.uri);
eo->id, ch->ch_name);
eo->putref(eo);
continue; // skip to next
/* No now */
} else if ( ebc->start > dispatch_clock ) {
ec->next = ebc;
next = ebc->start;
ch->ch_epg_next = ebc;
next = ebc->start;
/* Now/Next */
} else {
ec->now = ebc;
ec->next = (epg_broadcast_t*)RB_NEXT(eo, glink);
next = ebc->stop;
ch->ch_epg_now = ebc;
ch->ch_epg_next = (epg_broadcast_t*)RB_NEXT(eo, glink);
next = ebc->stop;
}
break;
}
tvhlog(LOG_DEBUG, "epg", "now/next %lu/%lu set on %s",
ec->now ? ec->now->_.id : 0,
ec->next ? ec->next->_.id : 0,
ec->_.uri);
ch->ch_epg_now ?: 0,
ch->ch_epg_next ?: 0,
ch->ch_name);
/* re-arm */
if ( next ) {
tvhlog(LOG_DEBUG, "epg", "arm channel timer @ %lu for %s",
next, ec->_.uri);
gtimer_arm_abs(&ec->expire, _epg_channel_timer_callback, ec, next);
next, ch->ch_name);
gtimer_arm_abs(&ch->ch_epg_timer, _epg_channel_timer_callback, ch, next);
}
/* Update HTSP */
if ( (cur != ec->now) && ec->channel ) {
if ( cur != ch->ch_epg_now ) {
tvhlog(LOG_DEBUG, "epg", "inform HTSP of now event change on %s",
ec->_.uri);
htsp_channel_update_current(ec->channel);
ch->ch_name);
htsp_channel_update_current(ch);
}
}
static void _epg_channel_destroy ( epg_object_t *eo )
void epg_channel_unlink ( channel_t *ch )
{
epg_object_t *ebc;
epg_channel_t *ec = (epg_channel_t*)eo;
if (ec->channel) {
tvhlog(LOG_CRIT, "epg", "attempt to destroy mapped channel");
assert(0);
epg_object_t *eo;
while ( (eo = RB_FIRST(&ch->ch_epg_schedule)) ) {
RB_REMOVE(&ch->ch_epg_schedule, eo, glink);
}
#if TODO_WHAT_SHOULD_BE_DONE
if (RB_FIRST(&ec->schedule)) {
tvhlog(LOG_CRIT, "epg", "attempt to destroy channel with schedule");
assert(0);
}
#endif
_epg_object_destroy(eo, &epg_channels);
// TODO: should we be doing this?
while ((ebc = RB_FIRST(&ec->schedule))) {
RB_REMOVE(&ec->schedule, ebc, glink);
ebc->putref(ebc);
}
gtimer_disarm(&ec->expire);
if (ec->name) free(ec->name);
#if TODO_NOT_IMPLEMENTED
if (ec->sname) free(ec->sname);
if (ec->sid) free(ec->sid);
#endif
free(ec);
}
epg_channel_t* epg_channel_find_by_uri
( const char *uri, int create, int *save )
{
int save2 = 0;
static epg_object_t *skel = NULL;
if ( !skel ) {
skel = calloc(1, sizeof(epg_channel_t));
skel->destroy = _epg_channel_destroy;
}
epg_channel_t *ec = (epg_channel_t*)
_epg_object_find_by_uri(uri, create, &save2,
&epg_channels, &skel);
if (save2) {
LIST_INSERT_HEAD(&epg_channel_unmapped, ec, umlink);
*save |= 1;
}
return ec;
}
epg_channel_t *epg_channel_find_by_id ( uint64_t id )
{
return (epg_channel_t*)_epg_object_find_by_id(id, &epg_channels);
}
int epg_channel_set_name ( epg_channel_t *channel, const char *name )
{
int save = 0;
channel_t *ch;
if ( !channel || !name ) return 0;
if ( !channel->name || strcmp(channel->name, name) ) {
if (channel->name) free(channel->name);
channel->name = strdup(name);
// TODO: need to allow overriding of EIT created channels
if ( !channel->channel ) {
LIST_FOREACH(ch, &channel_unmapped, ch_eulink) {
if ( _epg_channel_cmp(channel, ch) ) {
epg_channel_set_channel(channel, ch);
break;
}
}
}
// pass through
if ( channel->channel ) {
save |= channel_rename(channel->channel, name);
}
save |= 1;
}
return save;
}
int epg_channel_set_icon ( epg_channel_t *channel, const char *icon )
{
int save = 0;
if ( !channel | !icon ) return 0;
if ( !channel->icon || strcmp(channel->icon, icon) ) {
if ( channel->icon ) free(channel->icon);
channel->icon = strdup(icon);
// pass through
if ( channel->channel ) {
channel_set_icon(channel->channel, icon);
}
save = 1;
}
return save;
}
int epg_channel_set_channel ( epg_channel_t *ec, channel_t *ch )
{
int save = 0;
if ( !ec ) return 0;
if ( ec->channel != ch ) {
if (ec->channel) {
tvhlog(LOG_DEBUG, "epg", "unlink channels %-30s -> %s",
ec->_.uri, ec->channel->ch_name);
channel_set_epg_source(ec->channel, NULL);
LIST_INSERT_HEAD(&channel_unmapped, ec->channel, ch_eulink);
} else {
LIST_REMOVE(ec, umlink);
}
ec->channel = ch;
if (!ch) {
LIST_INSERT_HEAD(&epg_channel_unmapped, ec, umlink);
ec->_.putref((epg_object_t*)ec);
} else {
tvhlog(LOG_DEBUG, "epg", "link channels %-30s -> %s",
ec->_.uri, ch->ch_name);
channel_set_epg_source(ch, ec);
ec->_.getref((epg_object_t*)ec);
}
save |= 1;
}
return save;
gtimer_disarm(&ch->ch_epg_timer);
}
epg_broadcast_t *epg_channel_get_broadcast
( epg_channel_t *channel, time_t start, time_t stop, int create, int *save )
( channel_t *ch, time_t start, time_t stop, int create, int *save )
{
int save2 = 0;
epg_broadcast_t *ebc;
static epg_broadcast_t *skel;
if ( !channel || !start || !stop ) return NULL;
if ( !ch || !start || !stop ) return NULL;
if ( stop <= start ) return NULL;
if ( stop < dispatch_clock ) return NULL;
if ( skel == NULL ) skel = calloc(1, sizeof(epg_broadcast_t));
skel->channel = channel;
skel->channel = ch;
skel->start = start;
skel->stop = stop;
skel->_.id = _epg_object_idx;
skel->_.type = EPG_BROADCAST;
skel->_.destroy = _epg_broadcast_destroy;
ebc = (epg_broadcast_t*)
_epg_object_find(create, &save2, &channel->schedule,
_epg_object_find(create, &save2, &ch->ch_epg_schedule,
(epg_object_t**)&skel, _ebc_win_cmp);
if (save2) {
ebc->_.getref((epg_object_t*)ebc);
/* New current/next */
if ( (RB_FIRST(&channel->schedule) == (epg_object_t*)ebc) ||
(channel->now &&
RB_NEXT((epg_object_t*)channel->now, glink) == (epg_object_t*)ebc) ) {
_epg_channel_timer_callback(channel);
if ( (RB_FIRST(&ch->ch_epg_schedule) == (epg_object_t*)ebc) ||
(ch->ch_epg_now &&
RB_NEXT((epg_object_t*)ch->ch_epg_now, glink) == (epg_object_t*)ebc) ) {
_epg_channel_timer_callback(ch);
}
*save |= 1;
}
return ebc;
}
htsmsg_t *epg_channel_serialize ( epg_channel_t *channel )
{
htsmsg_t *m;
if (!channel || !channel->_.uri) return NULL;
m = htsmsg_create_map();
htsmsg_add_str(m, "uri", channel->_.uri);
if (channel->name)
htsmsg_add_str(m, "name", channel->name);
if (channel->channel)
htsmsg_add_u32(m, "channel", channel->channel->ch_id);
// TODO: other data
return m;
}
epg_channel_t *epg_channel_deserialize ( htsmsg_t *m, int create, int *save )
{
epg_channel_t *ec;
channel_t *ch;
uint32_t u32;
const char *str;
if ( !(str = htsmsg_get_str(m, "uri")) ) return NULL;
if ( !(ec = epg_channel_find_by_uri(str, create, save)) ) return NULL;
if ( (str = htsmsg_get_str(m, "name")) )
*save |= epg_channel_set_name(ec, str);
if ( !htsmsg_get_u32(m, "channel", &u32) )
if ( (ch = channel_find_by_identifier(u32)) )
epg_channel_set_channel(ec, ch);
// TODO: this call needs updating
return ec;
}
/* **************************************************************************
* Channel mapping
* *************************************************************************/
void epg_channel_map_add ( channel_t *ch )
{
epg_channel_t *ec;
LIST_FOREACH(ec, &epg_channel_unmapped, umlink) {
if ( _epg_channel_cmp(ec, ch) ) {
epg_channel_set_channel(ec, ch);
return;
}
}
LIST_INSERT_HEAD(&channel_unmapped, ch, ch_eulink);
}
void epg_channel_map_rem ( channel_t *ch )
{
if (ch->ch_epg_channel) {
epg_channel_set_channel(ch->ch_epg_channel, NULL);
} else {
LIST_REMOVE(ch, ch_eulink);
}
}
void epg_channel_map_mod ( channel_t *ch )
{
// If already mapped, ignore
if (!ch->ch_epg_channel) epg_channel_map_add(ch);
}
/* **************************************************************************
* Querying
* *************************************************************************/
@ -1344,13 +1129,13 @@ static void _eqr_add ( epg_query_result_t *eqr, epg_broadcast_t *e )
}
static void _eqr_add_channel
( epg_query_result_t *eqr, epg_channel_t *ec )
( epg_query_result_t *eqr, channel_t *ch )
{
epg_object_t *eo;
epg_broadcast_t *ebc;
RB_FOREACH(eo, &ec->schedule, glink) {
RB_FOREACH(eo, &ch->ch_epg_schedule, glink) {
ebc = (epg_broadcast_t*)eo;
if ( ebc->episode && ebc->channel ) _eqr_add(eqr, ebc);
if ( ebc->episode ) _eqr_add(eqr, ebc);
}
}
@ -1358,20 +1143,18 @@ void epg_query0
( epg_query_result_t *eqr, channel_t *channel, channel_tag_t *tag,
uint8_t contentgroup, const char *title )
{
epg_object_t *ec;
/* Clear (just incase) */
memset(eqr, 0, sizeof(epg_query_result_t));
/* All channels */
if (!channel) {
RB_FOREACH(ec, &epg_channels, glink) {
_eqr_add_channel(eqr, (epg_channel_t*)ec);
RB_FOREACH(channel, &channel_name_tree, ch_name_link) {
_eqr_add_channel(eqr, channel);
}
/* Single channel */
} else if ( channel->ch_epg_channel ) {
_eqr_add_channel(eqr, channel->ch_epg_channel);
} else if ( channel ) {
_eqr_add_channel(eqr, channel);
}
return;

View file

@ -63,6 +63,16 @@ typedef struct epg_broadcast_tree epg_broadcast_tree_t;
* Generic Object
* ***********************************************************************/
/* Object type */
typedef enum epg_object_type
{
EPG_UNDEF,
EPG_BRAND,
EPG_SEASON,
EPG_EPISODE,
EPG_BROADCAST
} epg_object_type_t;
/* Object */
typedef struct epg_object
{
@ -70,8 +80,9 @@ typedef struct epg_object
LIST_ENTRY(epg_object) hlink; ///< Global (ID) hash link
LIST_ENTRY(epg_object) ulink; ///< Global unref'd link
char *uri; ///< Unique ID (from grabber)
epg_object_type_t type; ///< Specific object type
uint64_t id; ///< Internal ID
char *uri; ///< Unique ID (from grabber)
int refcount; ///< Reference counting
void (*getref) ( epg_object_t* ); ///< Get a reference

View file

@ -4,10 +4,13 @@
#include "htsmsg.h"
#include "settings.h"
#include "tvheadend.h"
#include "src/queue.h"
#include "epg.h"
#include "epggrab.h"
#include "epggrab/pyepg.h"
#include "epggrab/eit.h"
#include "epggrab/xmltv.h"
#include "epggrab/pyepg.h"
#include "channels.h"
/* Thread protection */
int epggrab_confver;
@ -22,8 +25,8 @@ epggrab_module_t* epggrab_module;
epggrab_sched_list_t epggrab_schedule;
/* Modules */
epggrab_module_t* epggrab_module_pyepg;
epggrab_module_t* epggrab_module_xmltv;
LIST_HEAD(epggrab_module_list, epggrab_module);
struct epggrab_module_list epggrab_modules;
/* Internal prototypes */
static void* _epggrab_thread ( void* );
@ -45,8 +48,11 @@ void epggrab_init ( void )
epggrab_module = NULL; // disabled
/* Initialise modules */
epggrab_module_pyepg = pyepg_init();
epggrab_module_xmltv = xmltv_init();
LIST_INSERT_HEAD(&epggrab_modules, eit_init(), glink);
#if TODO_XMLTV_SUPPORT
LIST_INSERT_HEAD(&epggrab_modules, xmltv_init(), glink);
#endif
LIST_INSERT_HEAD(&epggrab_modules, pyepg_init(), glink);
/* Start thread */
pthread_t tid;
@ -190,11 +196,125 @@ void* _epggrab_thread ( void* p )
epggrab_module_t* epggrab_module_find_by_name ( const char *name )
{
if ( strcmp(name, "pyepg") == 0 ) return epggrab_module_pyepg;
if ( strcmp(name, "xmltv") == 0 ) return epggrab_module_xmltv;
epggrab_module_t *m;
LIST_FOREACH(m, &epggrab_modules, glink) {
if ( !strcmp(m->name(), name) ) return m;
}
return NULL;
}
/* **************************************************************************
* Channel handling
*
* TODO: this is all a bit messy (too much indirection?) but it works!
*
* TODO: need to save mappings to disk
*
* TODO: probably want rules on what can be updated etc...
* *************************************************************************/
static int _ch_id_cmp ( void *a, void *b )
{
return strcmp(((epggrab_channel_t*)a)->id,
((epggrab_channel_t*)b)->id);
}
static channel_t *_channel_find ( epggrab_channel_t *ch )
{
channel_t *ret = NULL;
if (ch->name) ret = channel_find_by_name(ch->name, 0, 0);
return ret;
}
static int _channel_match ( epggrab_channel_t *egc, channel_t *ch )
{
/* Already linked */
if ( egc->channel ) return 0;
/* Name match */
if ( !strcmp(ch->ch_name, egc->name) ) return 1;
return 0;
}
void epggrab_channel_add ( channel_t *ch )
{
epggrab_module_t *m;
LIST_FOREACH(m, &epggrab_modules, glink) {
if (m->ch_add) m->ch_add(ch);
}
}
void epggrab_channel_rem ( channel_t *ch )
{
epggrab_module_t *m;
LIST_FOREACH(m, &epggrab_modules, glink) {
if (m->ch_rem) m->ch_rem(ch);
}
}
void epggrab_channel_mod ( channel_t *ch )
{
epggrab_module_t *m;
LIST_FOREACH(m, &epggrab_modules, glink) {
if (m->ch_mod) m->ch_mod(ch);
}
}
epggrab_channel_t *epggrab_module_channel_create
( epggrab_channel_tree_t *tree, epggrab_channel_t *iskel )
{
epggrab_channel_t *egc;
static epggrab_channel_t *skel = NULL;
if (!skel) skel = calloc(1, sizeof(epggrab_channel_t));
skel->id = iskel->id;
skel->name = iskel->name;
egc = RB_INSERT_SORTED(tree, skel, glink, _ch_id_cmp);
if ( egc == NULL ) {
skel->id = strdup(skel->id);
skel->name = strdup(skel->name);
skel->channel = _channel_find(skel);
egc = skel;
skel = NULL;
}
return egc;
}
epggrab_channel_t *epggrab_module_channel_find
( epggrab_channel_tree_t *tree, const char *id )
{
epggrab_channel_t skel;
skel.id = (char*)id;
return RB_FIND(tree, &skel, glink, _ch_id_cmp);
}
void epggrab_module_channel_add ( epggrab_channel_tree_t *tree, channel_t *ch )
{
epggrab_channel_t *egc;
RB_FOREACH(egc, tree, glink) {
if (_channel_match(egc, ch) ) {
egc->channel = ch;
break;
}
}
}
void epggrab_module_channel_rem ( epggrab_channel_tree_t *tree, channel_t *ch )
{
epggrab_channel_t *egc;
RB_FOREACH(egc, tree, glink) {
if (egc->channel == ch) {
egc->channel = NULL;
break;
}
}
}
void epggrab_module_channel_mod ( epggrab_channel_tree_t *tree, channel_t *ch )
{
epggrab_module_channel_add(tree, ch);
}
/* **************************************************************************
* Configuration handling
* *************************************************************************/

View file

@ -27,6 +27,21 @@ typedef struct epggrab_stats
epggrab_stats_part_t broadcasts;
} epggrab_stats_t;
/*
* Grab channel
*/
typedef struct epggrab_channel
{
char *id; ///< Grabber's ID
char *name; ///< Channel name
struct channel *channel; ///< Mapped channel
// TODO: I think we might need a list of channels!
RB_ENTRY(epggrab_channel) glink; ///< Global link
} epggrab_channel_t;
RB_HEAD(epggrab_channel_tree, epggrab_channel);
typedef struct epggrab_channel_tree epggrab_channel_tree_t;
/*
* Grabber base class
*/
@ -40,6 +55,8 @@ typedef struct epggrab_module
void (*ch_add) ( struct channel *ch );
void (*ch_rem) ( struct channel *ch );
void (*ch_mod) ( struct channel *ch );
LIST_ENTRY(epggrab_module) glink; ///< Global list link
} epggrab_module_t;
/*
@ -97,4 +114,14 @@ void epggrab_channel_add ( struct channel *ch );
void epggrab_channel_rem ( struct channel *ch );
void epggrab_channel_mod ( struct channel *ch );
/*
* Module specific channel handling
*/
void epggrab_module_channel_add ( epggrab_channel_tree_t *tree, struct channel *ch );
void epggrab_module_channel_rem ( epggrab_channel_tree_t *tree, struct channel *ch );
void epggrab_module_channel_mod ( epggrab_channel_tree_t *tree, struct channel *ch );
epggrab_channel_t *epggrab_module_channel_create ( epggrab_channel_tree_t *tree, epggrab_channel_t *ch );
epggrab_channel_t *epggrab_module_channel_find ( epggrab_channel_tree_t *tree, const char *id );
#endif /* __EPGGRAB_H__ */

View file

@ -43,7 +43,6 @@ void eit_callback ( channel_t *ch, int id, time_t start, time_t stop,
const char *extitem, const char *extdesc,
const char *exttext ) {
int save = 0;
epg_channel_t *ec;
epg_broadcast_t *ebc;
epg_episode_t *ee;
const char *summary = NULL;
@ -53,17 +52,8 @@ void eit_callback ( channel_t *ch, int id, time_t start, time_t stop,
/* Disabled? */
//if (epggrab_eit_disabled) return;
/* Channel */
ec = ch->ch_epg_channel;
if (!ec) {
ec = epg_channel_find_by_uri(ch->ch_name, 1, &save);
if (ec)
epg_channel_set_channel(ec, ch);
}
if (!ec) return;
/* Find broadcast */
ebc = epg_channel_get_broadcast(ch->ch_epg_channel, start, stop, 1, &save);
ebc = epg_channel_get_broadcast(ch, start, stop, 1, &save);
if (!ebc) return;
/* Create episode URI */

View file

@ -23,7 +23,7 @@
epggrab_module_t *eit_init ( void );
void eit_callback ( channel_t *ch, int id, time_t start, time_t stop,
void eit_callback ( struct channel *ch, int id, time_t start, time_t stop,
const char *title, const char *desc,
const char *extitem, const char *extdesc,
const char *exttext );

View file

@ -26,6 +26,44 @@
#include "spawn.h"
#include "epg.h"
#include "epggrab/pyepg.h"
#include "channels.h"
/* **************************************************************************
* Channels
* *************************************************************************/
epggrab_channel_tree_t _pyepg_channels;
static channel_t *_pyepg_channel_create ( epggrab_channel_t *skel )
{
epggrab_channel_t *ch
= epggrab_module_channel_create(&_pyepg_channels, skel);
if (ch) return ch->channel;
return NULL;
}
static channel_t *_pyepg_channel_find ( const char *id )
{
epggrab_channel_t *ch
= epggrab_module_channel_find(&_pyepg_channels, id);
if (ch) return ch->channel;
return NULL;
}
static void _pyepg_channel_add ( channel_t *ch )
{
epggrab_module_channel_add(&_pyepg_channels, ch);
}
static void _pyepg_channel_rem ( channel_t *ch )
{
epggrab_module_channel_rem(&_pyepg_channels, ch);
}
static void _pyepg_channel_mod ( channel_t *ch )
{
epggrab_module_channel_mod(&_pyepg_channels, ch);
}
/* **************************************************************************
* Parsing
@ -47,25 +85,26 @@ static int _pyepg_parse_channel ( htsmsg_t *data, epggrab_stats_t *stats )
{
int save = 0;
htsmsg_t *attr, *tags;
const char *id, *name = NULL;
epg_channel_t *channel;
const char *icon;
channel_t *ch;
epggrab_channel_t skel;
if ( data == NULL ) return 0;
if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0;
if ((id = htsmsg_get_str(attr, "id")) == NULL) return 0;
if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0;
if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0;
if ((skel.id = (char*)htsmsg_get_str(attr, "id")) == NULL) return 0;
if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0;
skel.name = (char*)htsmsg_xml_get_cdata_str(tags, "name");
icon = htsmsg_xml_get_cdata_str(tags, "image");
/* Find channel */
if ((channel = epg_channel_find_by_uri(id, 1, &save)) == NULL) return 0;
stats->channels.total++;
if (save) stats->channels.created++;
ch = _pyepg_channel_create(&skel);
/* Set name */
name = htsmsg_xml_get_cdata_str(tags, "name");
if ( name ) save |= epg_channel_set_name(channel, name);
if (save) stats->channels.modified++;
/* Update values */
if (ch) {
// TODO: set the name
//if (skel.name) save |= channel_rename(ch, skel.name);
if (icon) channel_set_icon(ch, icon);
}
return save;
}
@ -245,7 +284,7 @@ static int _pyepg_parse_episode ( htsmsg_t *data, epggrab_stats_t *stats )
}
static int _pyepg_parse_broadcast
( htsmsg_t *data, epg_channel_t *channel, epggrab_stats_t *stats )
( htsmsg_t *data, channel_t *channel, epggrab_stats_t *stats )
{
int save = 0;
htsmsg_t *attr;//, *tags;
@ -289,19 +328,21 @@ static int _pyepg_parse_schedule ( htsmsg_t *data, epggrab_stats_t *stats )
int save = 0;
htsmsg_t *attr, *tags;
htsmsg_field_t *f;
epg_channel_t *channel;
channel_t *channel;
const char *str;
if ( data == NULL ) return 0;
if ((attr = htsmsg_get_map(data, "attrib")) == NULL) return 0;
if ((str = htsmsg_get_str(attr, "channel")) == NULL) return 0;
if ((channel = epg_channel_find_by_uri(str, 0, NULL)) == NULL) return 0;
if ((channel = _pyepg_channel_find(str)) == NULL) return 0;
if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0;
stats->channels.total++;
HTSMSG_FOREACH(f, tags) {
if (strcmp(f->hmf_name, "broadcast") == 0) {
save |= _pyepg_parse_broadcast(htsmsg_get_map_by_field(f), channel, stats);
save |= _pyepg_parse_broadcast(htsmsg_get_map_by_field(f),
channel, stats);
}
}
@ -333,7 +374,6 @@ static int _pyepg_parse_epg ( htsmsg_t *data, epggrab_stats_t *stats )
return save;
}
// TODO: add stats updating
static int _pyepg_parse ( htsmsg_t *data, epggrab_stats_t *stats )
{
htsmsg_t *tags, *epg;
@ -341,8 +381,6 @@ static int _pyepg_parse ( htsmsg_t *data, epggrab_stats_t *stats )
if ((tags = htsmsg_get_map(data, "tags")) == NULL) return 0;
// TODO: might be a better way to do this using DTD definition?
/* PyEPG format */
if ((epg = htsmsg_get_map(tags, "epg")) != NULL)
return _pyepg_parse_epg(epg, stats);
@ -419,6 +457,9 @@ epggrab_module_t* pyepg_init ( void )
pyepg_module.grab = _pyepg_grab;
pyepg_module.parse = _pyepg_parse;
pyepg_module.name = _pyepg_name;
pyepg_module.ch_add = _pyepg_channel_add;
pyepg_module.ch_rem = _pyepg_channel_rem;
pyepg_module.ch_mod = _pyepg_channel_mod;
return &pyepg_module;
}

View file

@ -38,6 +38,7 @@
/* **************************************************************************
* Parsing
* *************************************************************************/
#if TODO_XMLTV_GRABBER
/**
* Hash the description to get a URI for episode
@ -425,3 +426,5 @@ epggrab_module_t* xmltv_init ( void )
xmltv_module.name = _xmltv_name;
return &xmltv_module;
}
#endif

View file

@ -404,8 +404,8 @@ main(int argc, char **argv)
capmt_init();
epg_init();
epggrab_init();
epg_init();
dvr_init();