]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Start work on epg load/save and also added back in initial xmltv parser.
authorAdam Sutton <dev@adamsutton.me.uk>
Tue, 22 May 2012 09:15:05 +0000 (10:15 +0100)
committerAdam Sutton <dev@adamsutton.me.uk>
Tue, 22 May 2012 09:15:05 +0000 (10:15 +0100)
Makefile
src/epg.c
src/epg.h
src/epggrab.c
src/epggrab/pyepg.h
src/epggrab/xmltv.c
src/epggrab/xmltv.h

index 76d32e4cc8229b438414ccf7510b340b9e9cb96b..7152da6f140fa50f64cfa1c28e312531d433b088 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -71,7 +71,9 @@ SRCS =  src/main.c \
        src/iptv_input.c \
        src/avc.c \
 
-SRCS += src/epggrab/pyepg.c
+SRCS += src/epggrab/pyepg.c\
+  src/epggrab/xmltv.c
+
 
 
 SRCS += src/plumbing/tsfix.c \
index f34a52a0cf1ca117afda2b4737772ba5a2fe48a2..d4a466e4e92293b624da6f20a23de24ff5948fea 100644 (file)
--- a/src/epg.c
+++ b/src/epg.c
@@ -148,12 +148,85 @@ static void _epg_dump ( void )
  * Setup / Update
  * *************************************************************************/
 
-void epg_init ( void )
-{
+static int _epg_write ( int fd, htsmsg_t *m )
+{
+  int ret = 1;
+  size_t msglen;
+  void *msgdata;
+  if (m) {
+    int r = htsmsg_binary_serialize(m, &msgdata, &msglen, 0x10000);
+    htsmsg_destroy(m);
+    if (!r) {
+      ssize_t w = write(fd, msgdata, msglen);
+      free(msgdata);
+      if(w == msglen) ret = 0;
+    }
+  }
+  if(ret) {
+    tvhlog(LOG_DEBUG, "epg", "failed to store epg to disk");
+    close(fd);
+    hts_settings_remove("epgdb");
+  }
+  return ret;
 }
 
 void epg_save ( void )
 {
+  int fd;
+  epg_brand_t   *eb;
+  epg_season_t  *es;
+  epg_episode_t *ee;
+  epg_channel_t *ec;
+  epg_broadcast_t *ebc;
+  
+  printf("EPG save\n");
+  // TODO: requires markers in the file or some other means of
+  //       determining where the various object types are?
+  fd = hts_settings_open_file(1, "epgdb");
+  RB_FOREACH(ec,  &epg_channels, ec_link) {
+    if (_epg_write(fd, epg_channel_serialize(ec))) return;
+  }
+  RB_FOREACH(eb,  &epg_brands, eb_link) {
+    if (_epg_write(fd, epg_brand_serialize(eb))) return;
+  }
+  RB_FOREACH(es,  &epg_seasons, es_link) {
+    if (_epg_write(fd, epg_season_serialize(es))) return;
+  }
+  RB_FOREACH(ee,  &epg_episodes, ee_link) {
+    if (_epg_write(fd, epg_episode_serialize(ee))) return;
+  }
+  RB_FOREACH(ec, &epg_channels, ec_link) {
+    RB_FOREACH(ebc, &ec->ec_schedule, eb_slink) {
+      if (_epg_write(fd, epg_broadcast_serialize(ebc))) return;
+    }
+  }
+}
+
+void epg_init ( void )
+{
+  struct stat st;
+  int fd = hts_settings_open_file(0, "epgdb");
+  size_t remain;
+  uint8_t *mem, *rp;
+  fstat(fd, &st);
+  rp = mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+  //TODO:if ( mem == MAP_FAILED ) return;
+  remain = st.st_size;
+  while ( remain > 4 ) {
+    int msglen = (rp[0] << 24) | (rp[1] << 16) | (rp[2] << 8) | rp[3];
+    printf("msglen = %d\n", msglen);
+    remain -= 4;
+    rp += 4;
+    htsmsg_t *m = htsmsg_binary_deserialize(rp, msglen, NULL);
+    if(m) {
+      htsmsg_print(m);
+      htsmsg_destroy(m);
+    }
+    rp += msglen;
+    remain -= msglen;
+  }
+  munmap(mem, st.st_size);
+  close(fd);
 }
 
 void epg_updated ( void )
@@ -306,6 +379,23 @@ int epg_brand_rem_episode ( epg_brand_t *brand, epg_episode_t *episode, int u )
   return save;
 }
 
+htsmsg_t *epg_brand_serialize ( epg_brand_t *brand )
+{
+  htsmsg_t *m;
+  if ( !brand ) return NULL;
+  m = htsmsg_create_map();
+  // TODO: ID
+  if (brand->eb_uri)
+    htsmsg_add_str(m, "uri", brand->eb_uri);
+  if (brand->eb_title)
+    htsmsg_add_str(m, "title",   brand->eb_title);
+  if (brand->eb_summary)
+    htsmsg_add_str(m, "summary", brand->eb_summary);
+  if (brand->eb_season_count)
+    htsmsg_add_u32(m, "season-count", brand->eb_season_count);
+  return m;
+}
+
 /* **************************************************************************
  * Season
  * *************************************************************************/
@@ -416,6 +506,25 @@ int epg_season_rem_episode
   return save;
 }
 
+htsmsg_t *epg_season_serialize ( epg_season_t *season )
+{
+  htsmsg_t *m;
+  if (!season) return NULL;
+  m = htsmsg_create_map();
+  if (season->es_uri)
+    htsmsg_add_str(m, "uri", season->es_uri);
+  if (season->es_summary)
+    htsmsg_add_str(m, "summary", season->es_summary);
+  if (season->es_number)
+    htsmsg_add_u32(m, "number", season->es_number);
+  if (season->es_episode_count)
+    htsmsg_add_u32(m, "episode-count", season->es_episode_count);
+  if (season->es_brand)
+    htsmsg_add_str(m, "brand-id", season->es_brand->eb_uri);
+  // TODO: change to ID
+  return m;
+}
+
 /* **************************************************************************
  * Episode
  * *************************************************************************/
@@ -596,6 +705,35 @@ int epg_episode_get_number_onscreen
   return i;
 }
 
+htsmsg_t *epg_episode_serialize ( epg_episode_t *episode )
+{
+  htsmsg_t *m;
+  if (!episode) return NULL;
+  m = htsmsg_create_map();
+  if (episode->ee_uri)
+    htsmsg_add_str(m, "uri", episode->ee_uri);
+  if (episode->ee_title)
+    htsmsg_add_str(m, "title", episode->ee_title);
+  if (episode->ee_subtitle)
+    htsmsg_add_str(m, "subtitle", episode->ee_subtitle);
+  if (episode->ee_summary)
+    htsmsg_add_str(m, "summary", episode->ee_summary);
+  if (episode->ee_description)
+    htsmsg_add_str(m, "description", episode->ee_description);
+  if (episode->ee_number)
+    htsmsg_add_u32(m, "number", episode->ee_number);
+  if (episode->ee_part_count && episode->ee_part_count) {
+    htsmsg_add_u32(m, "part-number", episode->ee_part_number);
+    htsmsg_add_u32(m, "part-count", episode->ee_part_count);
+  }
+  // TODO: use ids
+  if (episode->ee_brand)
+    htsmsg_add_str(m, "brand-id", episode->ee_brand->eb_uri);
+  if (episode->ee_season)
+    htsmsg_add_str(m, "season-id", episode->ee_season->es_uri);
+  return m;
+}
+
 /* **************************************************************************
  * Broadcast
  * *************************************************************************/
@@ -672,6 +810,23 @@ epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *broadcast )
   return RB_NEXT(broadcast, eb_slink);
 }
 
+htsmsg_t *epg_broadcast_serialize ( epg_broadcast_t *broadcast )
+{
+  htsmsg_t *m;
+  if (!broadcast) return NULL;
+  m = htsmsg_create_map();
+  htsmsg_add_u32(m, "id", broadcast->eb_id);
+  htsmsg_add_u32(m, "start", broadcast->eb_start);
+  htsmsg_add_u32(m, "stop", broadcast->eb_stop);
+  if (broadcast->eb_dvb_id)
+    htsmsg_add_u32(m, "dvb-id", broadcast->eb_dvb_id);
+  // TODO: add other metadata fields
+  // TODO: use ID
+  if (broadcast->eb_episode)
+    htsmsg_add_str(m, "episode-id", broadcast->eb_episode->ee_uri);
+  return m;
+}
+
 /* **************************************************************************
  * Channel
  * *************************************************************************/
@@ -732,6 +887,19 @@ epg_broadcast_t *epg_channel_get_current_broadcast ( epg_channel_t *channel )
   return RB_FIRST(&channel->ec_schedule);
 }
 
+htsmsg_t *epg_channel_serialize ( epg_channel_t *channel )
+{
+  htsmsg_t *m;
+  if (!channel) return NULL;
+  m = htsmsg_create_map();
+  if (channel->ec_uri)
+    htsmsg_add_str(m, "uri", channel->ec_uri);
+  if (channel->ec_channel)
+    htsmsg_add_u32(m, "channel-id", channel->ec_channel->ch_id);
+  // TODO: other data
+  return m;
+}
+
 /* **************************************************************************
  * Querying
  * *************************************************************************/
index af6537b977d0be3e38e6fd8a4be041c42d4d1404..354ba9be541bc2e6fad1a03c03f6523a6e413879 100644 (file)
--- a/src/epg.h
+++ b/src/epg.h
@@ -80,6 +80,10 @@ int epg_brand_add_episode      ( epg_brand_t *b, epg_episode_t *s, int u )
 int epg_brand_rem_episode      ( epg_brand_t *b, epg_episode_t *s, int u )
   __attribute__((warn_unused_result));
 
+/* Serialization */
+htsmsg_t    *epg_brand_serialize   ( epg_brand_t *b );
+epg_brand_t *epg_brand_deserialize ( htsmsg_t *m, int create );
+
 /* ************************************************************************
  * Season
  * ***********************************************************************/
@@ -119,6 +123,10 @@ int epg_season_add_episode       ( epg_season_t *s, epg_episode_t *e, int u )
 int epg_season_rem_episode       ( epg_season_t *s, epg_episode_t *e, int u )
   __attribute__((warn_unused_result));
 
+/* Serialization */
+htsmsg_t    *epg_season_serialize    ( epg_season_t *b );
+epg_season_t *epg_season_deserialize ( htsmsg_t *m, int create );
+
 /* ************************************************************************
  * Episode
  *
@@ -178,6 +186,9 @@ int epg_episode_rem_broadcast    ( epg_episode_t *e, epg_broadcast_t *b, int u )
 /* Acessors */
 int epg_episode_get_number_onscreen ( epg_episode_t *e, char *b, int c );
 
+/* Serialization */
+htsmsg_t      *epg_episode_serialize   ( epg_episode_t *b );
+epg_episode_t *epg_episode_deserialize ( htsmsg_t *m, int create );
 
 /* ************************************************************************
  * Broadcast - specific airing (channel & time) of an episode
@@ -226,6 +237,10 @@ int epg_broadcast_set_episode    ( epg_broadcast_t *b, epg_episode_t *e, int u )
 /* Accessors */
 epg_broadcast_t *epg_broadcast_get_next ( epg_broadcast_t *b );
 
+/* Serialization */
+htsmsg_t        *epg_broadcast_serialize   ( epg_broadcast_t *b );
+epg_broadcast_t *epg_broadcast_deserialize ( htsmsg_t *m, int create );
+
 /* ************************************************************************
  * Channel - provides mapping from EPG channels to real channels
  * ***********************************************************************/
@@ -257,6 +272,10 @@ int epg_channel_set_name ( epg_channel_t *c, const char *n )
 /* Accessors */
 epg_broadcast_t *epg_channel_get_current_broadcast ( epg_channel_t *c );
 
+/* Serialization */
+htsmsg_t      *epg_channel_serialize   ( epg_channel_t *b );
+epg_channel_t *epg_channel_deserialize ( htsmsg_t *m, int create );
+
 /* ************************************************************************
  * Querying
  * ***********************************************************************/
index b811291adab4dacbc77d8b10c6a8616f8747d5d0..9196d1a5648e630bb26f445726d742e562eaeae8 100644 (file)
@@ -7,6 +7,7 @@
 #include "epg.h"
 #include "epggrab.h"
 #include "epggrab/pyepg.h"
+#include "epggrab/xmltv.h"
 
 /* Thread protection */
 int                  epggrab_confver;
@@ -22,6 +23,7 @@ epggrab_sched_list_t epggrab_schedule;
 
 /* Modules */
 epggrab_module_t*    epggrab_module_pyepg;
+epggrab_module_t*    epggrab_module_xmltv;
 
 /* Internal prototypes */
 static void*  _epggrab_thread          ( void* );
@@ -44,6 +46,7 @@ void epggrab_init ( void )
 
   /* Initialise modules */
   epggrab_module_pyepg = pyepg_init();
+  epggrab_module_xmltv = xmltv_init();
 
   /* Start thread */
   pthread_t      tid;
@@ -163,6 +166,7 @@ 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;
   return NULL;
 }
 
index d7afe9bc82bc4df4ff1836ea548023fbb3abc15d..5e42a5658cbc9f759320c692fea69ffde0cfb155 100644 (file)
@@ -4,4 +4,9 @@
 
 #include "epggrab.h"
 
+#ifndef EPGGRAB_PYEPG_H
+#define EPGGRAB_PYEPG_H
+
 epggrab_module_t* pyepg_init ( void );
+
+#endif
index 73b5580cb9ff1eeefb91ae7ac37618e584c5d294..004012ed464f0cecaf24a04cd5a9cdf0dcd1fcf9 100644 (file)
 #include "xmltv.h"
 #include "spawn.h"
 
-/**
- *
- */
-LIST_HEAD(xmltv_grabber_list, xmltv_grabber);
-static struct xmltv_grabber_list xmltv_grabbers;
-
-typedef struct xmltv_grabber {
-  LIST_ENTRY(xmltv_grabber) xg_link;
-  char *xg_path;
-  time_t xg_mtime;
-
-  char *xg_version;
-  char *xg_description;
-
-  uint32_t xg_capabilities;
-#define XG_CAP_BASELINE     0x1
-#define XG_CAP_MANUALCONFIG 0x2
-#define XG_CAP_CACHE        0x4
-#define XG_CAP_APICONFIG    0x8
-
-  int xg_dirty;
-
-} xmltv_grabber_t;
-
-
-typedef struct parse_stats {
-  int ps_channels;
-  int ps_programmes;
-  int ps_events_created;
-
-} parse_stats_t;
-
-
-
-
-static xmltv_grabber_t *xg_current;
-
-
-uint32_t xmltv_grab_interval;
-uint32_t xmltv_grab_enabled;
-
-/* xmltv_channels is protected by global_lock */
-static struct xmltv_channel_tree xmltv_channels;
-struct xmltv_channel_list xmltv_displaylist;
-
-pthread_mutex_t xmltv_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t  xmltv_cond;
-static int xmltv_confver;
-
-static void xmltv_grabbers_index(void);
-static void xmltv_grabbers_load(void);
-static void xmltv_grabbers_save(void);
-
-/**
- *
- */
-static int
-xc_dn_cmp(const xmltv_channel_t *a, const xmltv_channel_t *b)
-{
-  return strcmp(a->xc_displayname, b->xc_displayname);
-}
-
-
-/**
- *
- */
-static void
-xc_set_displayname(xmltv_channel_t *xc, const char *name)
-{
-  if(xc->xc_displayname != NULL) {
-    LIST_REMOVE(xc, xc_displayname_link);
-    free(xc->xc_displayname);
-  }
-  if(name == NULL) {
-    xc->xc_displayname = NULL;
-    return;
-  }
-
-  xc->xc_displayname = strdup(name);
-  LIST_INSERT_SORTED(&xmltv_displaylist, xc, xc_displayname_link, xc_dn_cmp);
-}
-
-/**
- *
- */
-static void
-xmltv_load(void)
-{
-  htsmsg_t *l, *c;
-  htsmsg_field_t *f;
-  xmltv_channel_t *xc;
-
-  if((l = hts_settings_load("xmltv/channels")) == NULL)
-    return;
-
-  HTSMSG_FOREACH(f, l) {
-    if((c = htsmsg_get_map_by_field(f)) == NULL)
-      continue;
-    
-    xc = xmltv_channel_find(f->hmf_name, 1);
-    tvh_str_set(&xc->xc_icon, htsmsg_get_str(c, "icon"));
-    
-    xc_set_displayname(xc, htsmsg_get_str(c, "displayname"));
-  }
-  htsmsg_destroy(l);
-}
-
-
-/**
- *
- */
-static void
-xmltv_save(xmltv_channel_t *xc)
-{
-  htsmsg_t *m = htsmsg_create_map();
-
-  if(xc->xc_displayname != NULL)
-    htsmsg_add_str(m, "displayname", xc->xc_displayname);
-
-  if(xc->xc_icon != NULL)
-    htsmsg_add_str(m, "icon", xc->xc_icon);
-
-  hts_settings_save(m, "xmltv/channels/%s", xc->xc_identifier);
-  htsmsg_destroy(m);
-}
-
-
-/**
- *
- */
-static void
-xmltv_map_to_channel_by_name(xmltv_channel_t *xc)
-{
-  channel_t *ch, *next;
-
-  for(ch = LIST_FIRST(&channels_not_xmltv_mapped); ch != NULL; ch = next) {
-    next = LIST_NEXT(ch, ch_xc_link);
-    assert(ch != next);
-    if(!strcasecmp(xc->xc_displayname, ch->ch_name)) {
-      tvhlog(LOG_NOTICE, "xmltv", "Channel \"%s\" automapped to xmltv-channel "
-            "\"%s\" (name matches)", ch->ch_name, xc->xc_displayname);
-      channel_set_xmltv_source(ch, xc);
-    }
-  }
-}
-
+/* **************************************************************************
+ * Parsing
+ * *************************************************************************/
 
 /**
  *
  */
 static time_t
-xmltv_str2time(const char *str)
+_xmltv_str2time(const char *str)
 {
   struct tm tm;
   int tz, r;
@@ -212,129 +69,7 @@ xmltv_str2time(const char *str)
   }
 }
 
-
-/**
- *
- */
-static int
-xc_id_cmp(const xmltv_channel_t *a, const xmltv_channel_t *b)
-{
-  return strcmp(a->xc_identifier, b->xc_identifier);
-}
-
-
-/**
- *
- */
-xmltv_channel_t *
-xmltv_channel_find(const char *id, int create)
-{
-  xmltv_channel_t *xc;
-  static xmltv_channel_t *skel;
-
-  lock_assert(&global_lock);
-
-  if(skel == NULL)
-    skel = calloc(1, sizeof(xmltv_channel_t));
-
-  skel->xc_identifier = (char *)id;
-  if(!create)
-    return RB_FIND(&xmltv_channels, skel, xc_link, xc_id_cmp);
-  
-  xc = RB_INSERT_SORTED(&xmltv_channels, skel, xc_link, xc_id_cmp);
-  if(xc == NULL) {
-    /* New entry was inserted */
-    xc = skel;
-    skel = NULL;
-    xc->xc_identifier = strdup(id);
-  }
-  return xc;
-}
-
-
-/**
- *
- */
-xmltv_channel_t *
-xmltv_channel_find_by_displayname(const char *name)
-{
-  xmltv_channel_t *xc;
-
-  lock_assert(&global_lock);
-
-  LIST_FOREACH(xc, &xmltv_displaylist, xc_displayname_link)
-    if(!strcmp(xc->xc_displayname, name))
-      break;
-  return xc;
-}
-
-
-/**
- * XXX: Move to libhts?
- */
-static const char *
-xmltv_get_cdata_by_tag(htsmsg_t *tags, const char *name)
-{
-  htsmsg_t *sub;
-  if((sub = htsmsg_get_map(tags, name)) == NULL)
-    return NULL;
-  return htsmsg_get_str(sub, "cdata");
-}
-
-
-/**
- * Parse a <channel> tag from xmltv
- */
-static void
-xmltv_parse_channel(htsmsg_t *body)
-{
-  htsmsg_t *attribs, *tags, *subtag;
-  const char *id, *name, *icon;
-  xmltv_channel_t *xc;
-  channel_t *ch;
-  int save = 0;
-
-  if(body == NULL)
-    return;
-
-  if((attribs = htsmsg_get_map(body, "attrib")) == NULL)
-    return;
-
-  tags = htsmsg_get_map(body, "tags");
-  
-  if((id = htsmsg_get_str(attribs, "id")) == NULL)
-    return;
-
-  xc = xmltv_channel_find(id, 1);
-
-  if(tags == NULL) 
-    return;
-
-  if((name = xmltv_get_cdata_by_tag(tags, "display-name")) != NULL) {
-
-    if(xc->xc_displayname == NULL || strcmp(xc->xc_displayname, name)) {
-      xc_set_displayname(xc, name);
-      xmltv_map_to_channel_by_name(xc);
-      save = 1;
-    }
-  }
-
-  if((subtag  = htsmsg_get_map(tags,    "icon"))   != NULL &&
-     (attribs = htsmsg_get_map(subtag,  "attrib")) != NULL &&
-     (icon    = htsmsg_get_str(attribs, "src"))    != NULL) {
-    
-    if(xc->xc_icon == NULL || strcmp(xc->xc_icon, icon)) {
-      tvh_str_set(&xc->xc_icon, icon);
-      
-      LIST_FOREACH(ch, &xc->xc_channels, ch_xc_link)
-       channel_set_icon(ch, icon);
-      save = 1;
-    }
-  }
-
-  if(save)
-    xmltv_save(xc);
-}
+#if TODO_XMLTV_EP_NUMBERING
 
 
 /**
@@ -471,654 +206,194 @@ get_episode_info(htsmsg_t *tags, epg_episode_t *ee)
   }
 }
 
+#endif
+
 /**
  * Parse tags inside of a programme
  */
-static void
-xmltv_parse_programme_tags(xmltv_channel_t *xc, htsmsg_t *tags, 
-                          time_t start, time_t stop, parse_stats_t *ps)
+static int
+_xmltv_parse_programme_tags(epg_channel_t *xc, htsmsg_t *tags, 
+                          time_t start, time_t stop)
 {
-  event_t *e;
-  channel_t *ch;
-  const char *title = xmltv_get_cdata_by_tag(tags, "title");
-  const char *desc  = xmltv_get_cdata_by_tag(tags, "desc");
-  const char *category = xmltv_get_cdata_by_tag(tags, "category");
-  int created;
-  epg_episode_t episode;
-
-  get_episode_info(tags, &episode);
-
-  LIST_FOREACH(ch, &xc->xc_channels, ch_xc_link) {
-    if((e = epg_event_create(ch, start, stop, -1, &created)) == NULL)
-      continue;
-
-    if(created)
-      ps->ps_events_created++;
-
-    int changed = 0;
-
-    if(title != NULL)
-      changed |= epg_event_set_title(e, title);
-
-    if(desc  != NULL)
-      changed |= epg_event_set_desc(e, desc);
-
-    if(category != NULL) {
-      uint8_t type = epg_content_group_find_by_name(category);
-      if(type)
-       changed |= epg_event_set_content_type(e, type);
-    }
-
-    changed |= epg_event_set_episode(e, &episode);
-
-    if(changed)
-      epg_event_updated(e);
-  }
-
-  free(episode.ee_onscreen);
+  int save = 0;
+  epg_broadcast_t *ebc;
+  epg_episode_t *ee;
+  const char *title = htsmsg_xml_get_cdata_str(tags, "title");
+  const char *desc  = htsmsg_xml_get_cdata_str(tags, "desc");
+#if TODO_EPG_GENRE
+  const char *category = htsmsg_xml_get_cdata_str(tags, "category");
+#endif
+
+  /* Generate an episode */
+  // TODO: hash the description?
+  ee = epg_episode_find_by_uri("TODO", 1);
+  if (title) save |= epg_episode_set_title(ee, title);
+  if (desc)  save |= epg_episode_set_description(ee, desc);
+#if TODO_EPG_GENRE
+  if (category) save |= epg_episode_set_genre(ee, category);
+#endif
+#if TODO_XMLTV_EP_NUMBERING
+  _get_episode_info(tags, &episode);
+#endif
+
+  /* Create broadcast */
+  ebc = epg_broadcast_find_by_time(xc, start, stop, 1);
+  save |= epg_broadcast_set_episode(ebc, ee, 1);
+  
+  return save;
 }
 
 
 /**
  * Parse a <programme> tag from xmltv
  */
-static void
-xmltv_parse_programme(htsmsg_t *body, parse_stats_t *ps)
+static int
+_xmltv_parse_programme(htsmsg_t *body)
 {
+  int save = 0;
   htsmsg_t *attribs, *tags;
   const char *s, *chid;
   time_t start, stop;
-  xmltv_channel_t *xc;
-
-  if(body == NULL)
-    return;
-
-  if((attribs = htsmsg_get_map(body, "attrib")) == NULL)
-    return;
-
-  if((tags = htsmsg_get_map(body, "tags")) == NULL)
-    return;
-
-  if((chid = htsmsg_get_str(attribs, "channel")) == NULL)
-    return;
-
-  if((s = htsmsg_get_str(attribs, "start")) == NULL)
-    return;
-  start = xmltv_str2time(s);
-
-  if((s = htsmsg_get_str(attribs, "stop")) == NULL)
-    return;
-  stop = xmltv_str2time(s);
-
-  if(stop <= start || stop < dispatch_clock)
-    return;
-
-  if((xc = xmltv_channel_find(chid, 0)) != NULL) {
-    xmltv_parse_programme_tags(xc, tags, start, stop, ps);
-  }
-}
-
-/**
- *
- */
-static void
-xmltv_parse_tv(htsmsg_t *body, parse_stats_t *ps)
-{
-  htsmsg_t *tags;
-  htsmsg_field_t *f;
-
-  if((tags = htsmsg_get_map(body, "tags")) == NULL)
-    return;
+  epg_channel_t *xc;
 
-  HTSMSG_FOREACH(f, tags) {
-    if(!strcmp(f->hmf_name, "channel")) {
-      xmltv_parse_channel(htsmsg_get_map_by_field(f));
-      ps->ps_channels++;
-    } else if(!strcmp(f->hmf_name, "programme")) {
-      xmltv_parse_programme(htsmsg_get_map_by_field(f), ps);
-      ps->ps_programmes++;
-    }
-  }
-}
-
-
-/**
- *
- */
-static void
-xmltv_parse(htsmsg_t *body, parse_stats_t *ps)
-{
-  htsmsg_t *tags, *tv;
+  if(body == NULL) return 0;
 
-  if((tags = htsmsg_get_map(body, "tags")) == NULL)
-    return;
+  if((attribs = htsmsg_get_map(body,    "attrib"))  == NULL) return 0;
+  if((tags    = htsmsg_get_map(body,    "tags"))    == NULL) return 0;
+  if((chid    = htsmsg_get_str(attribs, "channel")) == NULL) return 0;
+  if((xc      = epg_channel_find_by_uri(chid, 0))   == NULL) return 0;
+  if((s       = htsmsg_get_str(attribs, "start"))   == NULL) return 0;
+  start = _xmltv_str2time(s);
+  if((s       = htsmsg_get_str(attribs, "stop"))    == NULL) return 0;
+  stop = _xmltv_str2time(s);
 
-  if((tv = htsmsg_get_map(tags, "tv")) == NULL)
-    return;
+  if(stop <= start || stop < dispatch_clock) return 0;
 
-  xmltv_parse_tv(tv, ps);
+  _xmltv_parse_programme_tags(xc, tags, start, stop);
+  return save;
 }
 
 /**
- *
+ * Parse a <channel> tag from xmltv
  */
-static void
-xmltv_grab(const char *prog)
+static int
+_xmltv_parse_channel(htsmsg_t *body)
 {
-  int outlen;
-  char *outbuf;
-  htsmsg_t *body;
-  char errbuf[100];
-  time_t t1, t2;
-  parse_stats_t ps = {0};
-
-  time(&t1);
-  outlen = spawn_and_store_stdout(prog, NULL, &outbuf);
-  if(outlen < 1) {
-    tvhlog(LOG_ERR, "xmltv", "No output from \"%s\"", prog);
-    return;
-  }
+  int save =0;
+  htsmsg_t *attribs, *tags;/*, *subtag;*/
+  const char *id;/*TODO, *name, *icon;*/
+  epg_channel_t *xc;
 
-  time(&t2);
+  if(body == NULL) return 0;
 
-  tvhlog(LOG_DEBUG, "xmltv",
-        "%s: completed, took %ld seconds",
-        prog, t2 - t1);
-
-  body = htsmsg_xml_deserialize(outbuf, errbuf, sizeof(errbuf));
-  if(body == NULL) {
-    tvhlog(LOG_ERR, "xmltv", "Unable to parse output from \"%s\": %s",
-          prog, errbuf);
-    return;
+  if((attribs = htsmsg_get_map(body, "attrib"))  == NULL) return 0;
+  if((id      = htsmsg_get_str(attribs, "id"))   == NULL) return 0;
+  if((xc      = epg_channel_find_by_uri(id, 1))  == NULL) return 0;
+  if((tags    = htsmsg_get_map(body, "tags"))    == NULL) return 0;
+  // TODO: save in find_by_uri
+  
+#if TODO_EPG_CHANNEL_META
+  if((name = xmltv_get_cdata_by_tag(tags, "display-name")) != NULL) {
+    save |= epg_channel_set_name(xc, name);
   }
 
-
-  pthread_mutex_lock(&global_lock);
-  xmltv_parse(body, &ps);
-  pthread_mutex_unlock(&global_lock);
-
-  tvhlog(LOG_INFO, "xmltv",
-        "%s: Parsing completed. XML contained %d channels, %d events, "
-        "%d new events injected in EPG",
-        prog, 
-        ps.ps_channels,
-        ps.ps_programmes,
-        ps.ps_events_created);
-
-  htsmsg_destroy(body);
-}
-
-
-/**
- *
- */
-static void *
-xmltv_thread(void *aux)
-{
-  int confver = 0;
-  struct timespec ts;
-  char *p;
-
-  pthread_mutex_lock(&xmltv_mutex);
-
-  xmltv_grabbers_load();
-
-  xmltv_grabbers_index();
-
-  ts.tv_nsec = 0;
-  ts.tv_sec = 0;
-
-  while(1) {
-
-    while(confver == xmltv_confver) {
-
-      if(xg_current == NULL || !xmltv_grab_enabled) {
-       pthread_cond_wait(&xmltv_cond, &xmltv_mutex);
-       continue;
-      }
-      if(pthread_cond_timedwait(&xmltv_cond, &xmltv_mutex, &ts) == ETIMEDOUT)
-       break;
-    }
-
-    confver = xmltv_confver;
-
-    if(xmltv_grab_enabled == 0)
-      continue;
-
-    ts.tv_sec = time(NULL) + xmltv_grab_interval * 3600;
-
-    if(xg_current != NULL) {
-      tvhlog(LOG_INFO, "xmltv",
-            "Grabbing \"%s\" using command \"%s\"",
-            xg_current->xg_description, xg_current->xg_path);
-
-      /* Dup path so we can unlock */
-      p = strdup(xg_current->xg_path);
-
-      pthread_mutex_unlock(&xmltv_mutex);
+  if((subtag  = htsmsg_get_map(tags,    "icon"))   != NULL &&
+     (attribs = htsmsg_get_map(subtag,  "attrib")) != NULL &&
+     (icon    = htsmsg_get_str(attribs, "src"))    != NULL) {
+    
+    if(xc->xc_icon == NULL || strcmp(xc->xc_icon, icon)) {
+      tvh_str_set(&xc->xc_icon, icon);
       
-      xmltv_grab(p);
-
-      pthread_mutex_lock(&xmltv_mutex);
-      free(p);
+      LIST_FOREACH(ch, &xc->xc_channels, ch_xc_link)
+       channel_set_icon(ch, icon);
+      save = 1;
     }
   }
-  return NULL;
-}
-
-
-/**
- *
- */
-void
-xmltv_init(void)
-{
-  pthread_t ptid;
-
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
-  xmltv_grab_interval = 12; /* Default half a day */
-  xmltv_grab_enabled  = 1;  /* Default on */
-
-  /* Load all channels */
-  xmltv_load();
-  pthread_create(&ptid, &attr, xmltv_thread, NULL);
-}
-
-
-/**
- *
- */
-static void
-xmltv_grabber_destroy(xmltv_grabber_t *xg)
-{
-  LIST_REMOVE(xg, xg_link);
-  free(xg->xg_path);
-  free(xg->xg_version);
-  free(xg->xg_description);
-  free(xg);
-}
-
-/**
- *
- */
-static char *
-xmltv_grabber_get_description(const char *prog)
-{
-  int i, outlen;
-  char *outbuf;
-  const char *args[] = {prog, "--description", NULL};
-
-  if((outlen = spawn_and_store_stdout(prog, (void *)args, &outbuf)) < 1)
-    return NULL;
-
-  for(i = 0; i < outlen; i++)
-    if(outbuf[i] < 32)
-      outbuf[i] = 0;
-  return outbuf;
-}
-
-
-
-/**
- *
- */
-static char *
-xmltv_grabber_get_version(const char *prog)
-{
-  int outlen;
-  char *outbuf;
-  const char *args[] = {prog, "--version", NULL};
-
-  if((outlen = spawn_and_store_stdout(prog, (void *)args, &outbuf)) < 1)
-    return NULL;
-  return outbuf;
+#endif
+  return save;
 }
 
 /**
  *
  */
 static int
-xmltv_grabber_get_capabilities(const char *prog)
+_xmltv_parse_tv(htsmsg_t *body)
 {
-  int outlen;
-  char *outbuf;
-  int r;
-
-  const char *args[] = {prog, "--capabilities", NULL};
+  int save = 0;
+  htsmsg_t *tags;
+  htsmsg_field_t *f;
 
-  if((outlen = spawn_and_store_stdout(prog, (void *)args, &outbuf)) < 1)
+  if((tags = htsmsg_get_map(body, "tags")) == NULL)
     return 0;
 
-  r  = strstr(outbuf, "baseline")     ? XG_CAP_BASELINE     : 0;
-  r |= strstr(outbuf, "manualconfig") ? XG_CAP_MANUALCONFIG : 0;
-  r |= strstr(outbuf, "cache")        ? XG_CAP_CACHE        : 0;
-  r |= strstr(outbuf, "apiconfig")    ? XG_CAP_APICONFIG    : 0;
-  return r;
-}
-
-/**
- *
- */
-static int
-xg_desc_cmp(const xmltv_grabber_t *a, const xmltv_grabber_t *b)
-{
-  return strcmp(a->xg_description, b->xg_description);
-}
-
-/**
- *
- */
-static int
-xmltv_grabber_add(const char *path, time_t mtime)
-{
-  char *desc;
-  xmltv_grabber_t *xg;
-  int wasthisgrabber = 0;
-
-  LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
-    if(!strcmp(xg->xg_path, path))
-      break;
-
-  if(xg != NULL) {
-    if(xg->xg_mtime == mtime) {
-      xg->xg_dirty = 0;
-      return 0; /* Already there */
+  HTSMSG_FOREACH(f, tags) {
+    if(!strcmp(f->hmf_name, "channel")) {
+      save |= _xmltv_parse_channel(htsmsg_get_map_by_field(f));
+    } else if(!strcmp(f->hmf_name, "programme")) {
+      save |= _xmltv_parse_programme(htsmsg_get_map_by_field(f));
     }
-    wasthisgrabber = xg == xg_current;
-    xmltv_grabber_destroy(xg);
-    xg_current = NULL;
   }
-  if((desc = xmltv_grabber_get_description(path)) == NULL)
-    return 0; /* Probably not a working grabber */
-
-  xg = calloc(1, sizeof(xmltv_grabber_t));
-  xg->xg_path = strdup(path);
-  xg->xg_mtime = mtime;
-  xg->xg_description = strdup(desc);
-  xg->xg_capabilities = xmltv_grabber_get_capabilities(path);
-  tvh_str_set(&xg->xg_version, xmltv_grabber_get_version(path));
-  LIST_INSERT_SORTED(&xmltv_grabbers, xg, xg_link, xg_desc_cmp);
-  if(wasthisgrabber)
-    xg_current = xg;
-  return 1;
+  return save;
 }
 
 
 /**
  *
  */
-static xmltv_grabber_t *
-xmltv_grabber_find_by_path(const char *path)
+static int _xmltv_parse ( htsmsg_t *data )
 {
-  xmltv_grabber_t *xg;
-
-  LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
-    if(!strcmp(xg->xg_path, path))
-      break;
-  return xg;
-}
-
+  htsmsg_t *tags, *tv;
 
-/**
- *
- */
-static int
-xmltv_grabber_filter(const struct dirent *d)
-{
-  if(strncmp(d->d_name, "tv_grab_", 8))
+  if((tags = htsmsg_get_map(data, "tags")) == NULL)
     return 0;
-  return 1;
-
-}
-
 
-/**
- *
- */
-static int
-xmltv_scan_grabbers(const char *path)
-{
-  struct dirent **namelist;
-  int n, change = 0;
-  char fname[300];
-  struct stat st;
-
-  if((n = scandir(path, &namelist, xmltv_grabber_filter, NULL)) < 0)
+  if((tv = htsmsg_get_map(tags, "tv")) == NULL)
     return 0;
 
-  while(n--) {
-    snprintf(fname, sizeof(fname), "%s/%s", path, namelist[n]->d_name);
-    if(!stat(fname, &st))
-      change |= xmltv_grabber_add(fname, st.st_mtime);
-    free(namelist[n]);
-  }
-  free(namelist);
-  return change;
-}
-
-
-/**
- *
- */
-static void
-xmltv_grabbers_index(void)
-{
-  xmltv_grabber_t *xg, *next;
-  int change;
-  char *path, *p, *s;
-
-  LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
-    xg->xg_dirty = 1;
-
-  change =  xmltv_scan_grabbers("/bin");
-  change |= xmltv_scan_grabbers("/usr/bin");
-  change |= xmltv_scan_grabbers("/usr/local/bin");
-  // Arch linux puts xmltv grabbers in /usr/bin/perlbin/vendor
-  change |= xmltv_scan_grabbers("/usr/bin/perlbin/vendor"); 
-
-  if((path = getenv("PATH")) != NULL) {
-    p = path = strdup(path);
-    while((s = strsep(&p, ":")) != NULL)
-      change |= xmltv_scan_grabbers(s);
-    free(path);
-  }
-
-  for(xg = LIST_FIRST(&xmltv_grabbers); xg != NULL; xg = next) {
-    next = LIST_NEXT(xg, xg_link);
-    if(xg->xg_dirty) {
-      change = 1;
-      xmltv_grabber_destroy(xg);
-    }
-  }
-
-  if(change)
-    xmltv_grabbers_save();
-}
-
-/**
- *
- */
-static void
-xmltv_grabbers_load(void)
-{
-  xmltv_grabber_t *xg;
-  htsmsg_t *l, *c, *m;
-  htsmsg_field_t *f;
-  const char *path, *desc;
-  uint32_t u32;
-
-  if((m = hts_settings_load("xmltv/config")) == NULL)
-    return;
-
-  htsmsg_get_u32(m, "grab-interval", &xmltv_grab_interval);
-  htsmsg_get_u32(m, "grab-enabled", &xmltv_grab_enabled);
-
-  if((l = htsmsg_get_list(m, "grabbers")) != NULL) {
-
-    HTSMSG_FOREACH(f, l) {
-      if((c = htsmsg_get_map_by_field(f)) == NULL)
-       continue;
-
-      path = htsmsg_get_str(c, "path");
-      desc = htsmsg_get_str(c, "description");
-      if(path == NULL || desc == NULL)
-       continue;
-
-      xg = calloc(1, sizeof(xmltv_grabber_t));
-      xg->xg_path = strdup(path);
-      xg->xg_description = strdup(desc);
-
-      if(!htsmsg_get_u32(c, "mtime", &u32))
-       xg->xg_mtime = u32;
-
-      tvh_str_set(&xg->xg_version, htsmsg_get_str(c, "version"));
-      htsmsg_get_u32(c, "capabilities", &xg->xg_capabilities);
-      LIST_INSERT_SORTED(&xmltv_grabbers, xg, xg_link, xg_desc_cmp);
-    }
-  }
-
-  if((path = htsmsg_get_str(m, "current-grabber")) != NULL) {
-    if((xg = xmltv_grabber_find_by_path(path)) != NULL)
-      xg_current = xg;
-  }
-
-  htsmsg_destroy(m);
-}
-
-
-static void
-xmltv_grabbers_save(void)
-{
-  htsmsg_t *array, *e, *m;
-  xmltv_grabber_t *xg;
-
-  m = htsmsg_create_map();
-
-  array = htsmsg_create_list();
-
-  LIST_FOREACH(xg, &xmltv_grabbers, xg_link) {
-    e = htsmsg_create_map();
-    htsmsg_add_str(e, "path", xg->xg_path);
-    htsmsg_add_str(e, "description", xg->xg_description);
-    if(xg->xg_version != NULL)
-      htsmsg_add_str(e, "version", xg->xg_version);
-    htsmsg_add_u32(e, "mtime", xg->xg_mtime);
-    htsmsg_add_u32(e, "capabilities", xg->xg_capabilities);
-
-    htsmsg_add_msg(array, NULL, e);
-  }
-
-  htsmsg_add_msg(m, "grabbers", array);
-
-  htsmsg_add_u32(m, "grab-interval", xmltv_grab_interval);
-  htsmsg_add_u32(m, "grab-enabled", xmltv_grab_enabled);
-  if(xg_current != NULL)
-    htsmsg_add_str(m, "current-grabber", xg_current->xg_path);
-
-  hts_settings_save(m, "xmltv/config");
-  htsmsg_destroy(m);
+  return _xmltv_parse_tv(tv);
 }
 
+/* ************************************************************************
+ * Module Setup
+ * ***********************************************************************/
 
-/**
- *
- */
-htsmsg_t *
-xmltv_list_grabbers(void)
-{
-  htsmsg_t *array, *m;
-  xmltv_grabber_t *xg;
-
-  lock_assert(&xmltv_mutex);
-
-  xmltv_grabbers_index();
-
-  array = htsmsg_create_list();
-
-  LIST_FOREACH(xg, &xmltv_grabbers, xg_link) {
-    m = htsmsg_create_map();
-    htsmsg_add_str(m, "identifier", xg->xg_path);
-    htsmsg_add_str(m, "name", xg->xg_description);
-    htsmsg_add_str(m, "version", xg->xg_version ?: "Unknown version");
-    htsmsg_add_u32(m, "apiconfig", !!(xg->xg_capabilities & XG_CAP_APICONFIG));
-    htsmsg_add_msg(array, NULL, m);
-  }
-
-  return array;
-}
+// TODO: config
 
+static epggrab_module_t xmltv_module;
 
-/**
- *
- */
-const char *
-xmltv_get_current_grabber(void)
+static const char* _xmltv_name ( void )
 {
-  lock_assert(&xmltv_mutex);
-  return xg_current ? xg_current->xg_description : NULL;
+  return "xmltv";
 }
 
-
-/**
- *
- */
-void
-xmltv_set_current_grabber(const char *desc)
+static htsmsg_t* _xmltv_grab ( const char *iopts )
 {
-  xmltv_grabber_t *xg;
+  size_t outlen;
+  char *outbuf;
+  char errbuf[100];
+  const char *cmd = "/usr/bin/tv_grab_uk_rt";
 
-  lock_assert(&xmltv_mutex);
+  /* Debug */
+  tvhlog(LOG_DEBUG, "xmltv", "grab %s", cmd);
 
-  if(desc != NULL) {
-    LIST_FOREACH(xg, &xmltv_grabbers, xg_link)
-      if(!strcmp(xg->xg_description, desc))
-       break;
-    if(xg == NULL)
-      return;
-  } else {
-    xg = NULL;
+  /* Grab */
+  /* TODO: using hardcoded xmltv command at the moment */
+  outlen = spawn_and_store_stdout(cmd, NULL, &outbuf);
+  if ( outlen < 1 ) {
+    tvhlog(LOG_ERR, "xmltv", "no output detected");
+    return NULL;
   }
 
-  if(xg_current == xg)
-    return;
-
-  xg_current = xg;
-
-  xmltv_confver++;
-  pthread_cond_signal(&xmltv_cond);
-  xmltv_grabbers_save();
+  /* Extract */
+  return htsmsg_xml_deserialize(outbuf, errbuf, sizeof(errbuf));
 }
 
-/**
- *
- */
-void
-xmltv_set_grab_interval(int s)
+epggrab_module_t* xmltv_init ( void )
 {
-  lock_assert(&xmltv_mutex);
-
-  xmltv_grab_interval = s;
-  xmltv_confver++;
-  pthread_cond_signal(&xmltv_cond);
-  xmltv_grabbers_save();
-
-}
-
-/**
- *
- */
-void
-xmltv_set_grab_enable(int on)
-{
-  lock_assert(&xmltv_mutex);
-
-  xmltv_grab_enabled = on;
-  xmltv_confver++;
-  pthread_cond_signal(&xmltv_cond);
-  xmltv_grabbers_save();
+  xmltv_module.enable  = NULL;
+  xmltv_module.disable = NULL;
+  xmltv_module.grab    = _xmltv_grab;
+  xmltv_module.parse   = _xmltv_parse;
+  xmltv_module.name    = _xmltv_name;
+  return &xmltv_module;
 }
index 1081921920fa745f6d2fcb1e7a045d05c0879a43..3c501a13ae94512ee4994698b42204f654a29634 100644 (file)
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef EPG_XMLTV_H
-#define EPG_XMLTV_H
+#ifndef EPGGRAB_XMLTV_H
+#define EPGGRAB_XMLTV_H
 
-#include "channels.h"
+#include "epggrab.h"
 
-RB_HEAD(xmltv_channel_tree, xmltv_channel);
-LIST_HEAD(xmltv_channel_list, xmltv_channel);
+epggrab_module_t *xmltv_init ( void );
 
-/**
- * A channel in the XML-TV world
- */
-typedef struct xmltv_channel {
-  RB_ENTRY(xmltv_channel) xc_link;
-  char *xc_identifier;
-
-  LIST_ENTRY(xmltv_channel) xc_displayname_link;
-  char *xc_displayname;
-
-  char *xc_icon;
-
-  struct channel_list xc_channels;  /* Target channel(s) */
-
-} xmltv_channel_t;
-
-void xmltv_init(void);
-
-xmltv_channel_t *xmltv_channel_find(const char *id, int create);
-
-xmltv_channel_t *xmltv_channel_find_by_displayname(const char *name);
-
-htsmsg_t *xmltv_list_grabbers(void);
-
-const char *xmltv_get_current_grabber(void);
-void xmltv_set_current_grabber(const char *path);
-void xmltv_set_grab_interval(int s);
-void xmltv_set_grab_enable(int on);
-
-extern struct xmltv_channel_list xmltv_displaylist;
-extern uint32_t xmltv_grab_interval;
-extern uint32_t xmltv_grab_enabled;
-extern pthread_mutex_t xmltv_mutex;
-
-#endif /* EPG_XMLTV_H__ */
+#endif