diff --git a/src/epg.c b/src/epg.c
index a265313e..b4553f4c 100644
--- a/src/epg.c
+++ b/src/epg.c
@@ -16,16 +16,6 @@
* along with this program. If not, see .
*/
-/*
- * 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
#include
#include
@@ -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;
diff --git a/src/epg.h b/src/epg.h
index 0e83ae56..16a18f46 100644
--- a/src/epg.h
+++ b/src/epg.h
@@ -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
diff --git a/src/epggrab.c b/src/epggrab.c
index cebe43c2..8762f3cb 100644
--- a/src/epggrab.c
+++ b/src/epggrab.c
@@ -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
* *************************************************************************/
diff --git a/src/epggrab.h b/src/epggrab.h
index dfc1dba3..dbf961b3 100644
--- a/src/epggrab.h
+++ b/src/epggrab.h
@@ -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__ */
diff --git a/src/epggrab/eit.c b/src/epggrab/eit.c
index 34e2c3ae..19beac85 100644
--- a/src/epggrab/eit.c
+++ b/src/epggrab/eit.c
@@ -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 */
diff --git a/src/epggrab/eit.h b/src/epggrab/eit.h
index 1fc44528..3b0870c4 100644
--- a/src/epggrab/eit.h
+++ b/src/epggrab/eit.h
@@ -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 );
diff --git a/src/epggrab/pyepg.c b/src/epggrab/pyepg.c
index 79c6e7a6..6c52a911 100644
--- a/src/epggrab/pyepg.c
+++ b/src/epggrab/pyepg.c
@@ -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;
}
diff --git a/src/epggrab/xmltv.c b/src/epggrab/xmltv.c
index 42a4174c..970e8d77 100644
--- a/src/epggrab/xmltv.c
+++ b/src/epggrab/xmltv.c
@@ -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
diff --git a/src/main.c b/src/main.c
index 566c103d..ee6c4fc7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -404,8 +404,8 @@ main(int argc, char **argv)
capmt_init();
- epg_init();
epggrab_init();
+ epg_init();
dvr_init();