* 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 )
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
* *************************************************************************/
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
* *************************************************************************/
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
* *************************************************************************/
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
* *************************************************************************/
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
* *************************************************************************/
#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;
}
}
-
-/**
- *
- */
-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
/**
}
}
+#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;
}