From 5793c9df755646ae5d3a528cdf7f19066bc3d45e Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Fri, 29 Jun 2012 15:15:48 +0100 Subject: [PATCH] Migrated epgdb file handling routines to a separate file and change file postfix to something more sensible. --- Makefile | 1 + src/epg.c | 243 ------------------------------------------- src/epg.h | 1 - src/epgdb.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+), 244 deletions(-) create mode 100644 src/epgdb.c diff --git a/Makefile b/Makefile index 537b25e7..f4feb0c5 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ SRCS = src/main.c \ src/notify.c \ src/file.c \ src/epg.c \ + src/epgdb.c\ src/epggrab.c\ src/spawn.c \ src/packet.c \ diff --git a/src/epg.c b/src/epg.c index 1e7c0fef..ffe1ca38 100644 --- a/src/epg.c +++ b/src/epg.c @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -#include -#include #include #include #include @@ -33,13 +31,8 @@ #include "epg.h" #include "dvr/dvr.h" #include "htsp.h" -#include "htsmsg_binary.h" #include "epggrab.h" -/* EPG database file */ -#define EPG_DB_OLD "epgdb" -#define EPG_DB_NEW "epgdb.aps" - /* Broadcast hashing */ #define EPG_HASH_WIDTH 1024 #define EPG_HASH_MASK (EPG_HASH_WIDTH - 1) @@ -99,242 +92,6 @@ static int _episode_order ( const void *_a, const void *_b ) return a->epnum.p_num - b->epnum.p_num; } -/* ************************************************************************** - * Setup / Update - * *************************************************************************/ - -static void _epg_event_deserialize ( htsmsg_t *c, epggrab_stats_t *stats ) -{ - channel_t *ch; - epg_episode_t *ee; - epg_broadcast_t *ebc; - uint32_t ch_id = 0; - uint32_t e_start = 0; - uint32_t e_stop = 0; - uint32_t u32; - const char *title, *desc, *str; - char *uri; - int save = 0; - - /* Check key info */ - if(htsmsg_get_u32(c, "ch_id", &ch_id)) return; - if((ch = channel_find_by_identifier(ch_id)) == NULL) return; - if(htsmsg_get_u32(c, "start", &e_start)) return; - if(htsmsg_get_u32(c, "stop", &e_stop)) return; - if(!(title = htsmsg_get_str(c, "title"))) return; - - /* Create broadcast */ - save = 0; - ebc = epg_broadcast_find_by_time(ch, e_start, e_stop, 0, 1, &save); - if (!ebc) return; - if (save) stats->broadcasts.total++; - - /* Create episode */ - save = 0; - desc = htsmsg_get_str(c, "desc"); - uri = md5sum(desc ?: title); - ee = epg_episode_find_by_uri(uri, 1, &save); - free(uri); - if (!ee) return; - if (save) stats->episodes.total++; - if (title) - save |= epg_episode_set_title(ee, title); - if (desc) - save |= epg_episode_set_summary(ee, desc); - if (!htsmsg_get_u32(c, "episode", &u32)) - save |= epg_episode_set_number(ee, u32); - if (!htsmsg_get_u32(c, "part", &u32)) - save |= epg_episode_set_part(ee, u32, 0); - if (!htsmsg_get_u32(c, "season", &u32)) - ee->epnum.s_num = u32; - if ((str = htsmsg_get_str(c, "epname"))) - ee->epnum.text = strdup(str); - - /* Set episode */ - save |= epg_broadcast_set_episode(ebc, ee); -} - -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; - } - } else { - ret = 0; - } - if(ret) { - tvhlog(LOG_ERR, "epg", "failed to store epg to disk"); - close(fd); - hts_settings_remove(EPG_DB_NEW); - } - return ret; -} - -static int _epg_write_sect ( int fd, const char *sect ) -{ - htsmsg_t *m = htsmsg_create_map(); - htsmsg_add_str(m, "__section__", sect); - return _epg_write(fd, m); -} - -void epg_save ( void ) -{ - int fd; - epg_object_t *eo; - epg_broadcast_t *ebc; - channel_t *ch; - epggrab_stats_t stats; - - fd = hts_settings_open_file(1, EPG_DB_NEW); - - memset(&stats, 0, sizeof(stats)); - if ( _epg_write_sect(fd, "brands") ) return; - RB_FOREACH(eo, &epg_brands, uri_link) { - if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) return; - stats.brands.total++; - } - if ( _epg_write_sect(fd, "seasons") ) return; - RB_FOREACH(eo, &epg_seasons, uri_link) { - if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) return; - stats.seasons.total++; - } - if ( _epg_write_sect(fd, "episodes") ) return; - RB_FOREACH(eo, &epg_episodes, uri_link) { - if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) return; - stats.episodes.total++; - } - if ( _epg_write_sect(fd, "broadcasts") ) return; - RB_FOREACH(ch, &channel_name_tree, ch_name_link) { - RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) { - if (_epg_write(fd, epg_broadcast_serialize(ebc))) return; - stats.broadcasts.total++; - } - } - - /* Stats */ - tvhlog(LOG_INFO, "epg", "database saved"); - tvhlog(LOG_INFO, "epg", " brands %d", stats.brands.total); - tvhlog(LOG_INFO, "epg", " seasons %d", stats.seasons.total); - tvhlog(LOG_INFO, "epg", " episodes %d", stats.episodes.total); - tvhlog(LOG_INFO, "epg", " broadcasts %d", stats.broadcasts.total); -} - -void epg_init ( void ) -{ - int save, fd; - struct stat st; - size_t remain; - uint8_t *mem, *rp; - char *sect = NULL; - const char *s; - epggrab_stats_t stats; - int old = 0; - - /* Map file to memory */ - fd = hts_settings_open_file(0, EPG_DB_NEW); - if (fd < 0) fd = hts_settings_open_file(0, EPG_DB_OLD); - if ( fd < 0 ) { - tvhlog(LOG_DEBUG, "epg", "database does not exist"); - return; - } - if ( fstat(fd, &st) != 0 ) { - tvhlog(LOG_ERR, "epg", "failed to detect database size"); - return; - } - if ( !st.st_size ) { - tvhlog(LOG_DEBUG, "epg", "database is empty"); - return; - } - remain = st.st_size; - rp = mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - if ( mem == MAP_FAILED ) { - tvhlog(LOG_ERR, "epg", "failed to mmap database"); - return; - } - - /* Process */ - memset(&stats, 0, sizeof(stats)); - while ( remain > 4 ) { - - /* Get message length */ - int msglen = (rp[0] << 24) | (rp[1] << 16) | (rp[2] << 8) | rp[3]; - remain -= 4; - rp += 4; - - /* Extract message */ - htsmsg_t *m = htsmsg_binary_deserialize(rp, msglen, NULL); - - /* Process */ - if(m) { - - /* New section */ - s = htsmsg_get_str(m, "__section__"); - if (s) { - if (sect) free(sect); - sect = strdup(s); - - /* Assume OLD data */ - } else if ( !sect ) { - if (!old) { - old = 1; - tvhlog(LOG_INFO, "epg", "migrating old database"); - } - _epg_event_deserialize(m, &stats); - - /* Brand */ - } else if ( !strcmp(sect, "brands") ) { - if (epg_brand_deserialize(m, 1, &save)) stats.brands.total++; - - /* Season */ - } else if ( !strcmp(sect, "seasons") ) { - if (epg_season_deserialize(m, 1, &save)) stats.seasons.total++; - - /* Episode */ - } else if ( !strcmp(sect, "episodes") ) { - if (epg_episode_deserialize(m, 1, &save)) stats.episodes.total++; - - /* Broadcasts */ - } else if ( !strcmp(sect, "broadcasts") ) { - if (epg_broadcast_deserialize(m, 1, &save)) stats.broadcasts.total++; - - /* Unknown */ - } else { - tvhlog(LOG_DEBUG, "epg", "malformed database section [%s]", sect); - //htsmsg_print(m); - } - - /* Cleanup */ - htsmsg_destroy(m); - } - - /* Next */ - rp += msglen; - remain -= msglen; - } - if (sect) free(sect); - - /* Stats */ - tvhlog(LOG_INFO, "epg", "database loaded"); - tvhlog(LOG_INFO, "epg", " channels %d", stats.channels.total); - tvhlog(LOG_INFO, "epg", " brands %d", stats.brands.total); - tvhlog(LOG_INFO, "epg", " seasons %d", stats.seasons.total); - tvhlog(LOG_INFO, "epg", " episodes %d", stats.episodes.total); - tvhlog(LOG_INFO, "epg", " broadcasts %d", stats.broadcasts.total); - tvhlog(LOG_DEBUG, "epg", "next object id %"PRIu64, _epg_object_idx+1); - - /* Close file */ - munmap(mem, st.st_size); - close(fd); -} - void epg_updated ( void ) { epg_object_t *eo; diff --git a/src/epg.h b/src/epg.h index a40527e0..2575fad9 100644 --- a/src/epg.h +++ b/src/epg.h @@ -53,7 +53,6 @@ typedef struct epg_broadcast_tree epg_broadcast_tree_t; typedef struct epg_object_list epg_object_list_t; typedef struct epg_object_tree epg_object_tree_t; - /* ************************************************************************ * Generic Object * ***********************************************************************/ diff --git a/src/epgdb.c b/src/epgdb.c new file mode 100644 index 00000000..d9006aac --- /dev/null +++ b/src/epgdb.c @@ -0,0 +1,294 @@ +/* + * Electronic Program Guide - Database File functions + * Copyright (C) 2007 Andreas Ă–man + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "tvheadend.h" +#include "queue.h" +#include "htsmsg_binary.h" +#include "settings.h" +#include "epg.h" +#include "epggrab.h" + +#define EPG_DB_VERSION 2 + +extern epg_object_tree_t epg_brands; +extern epg_object_tree_t epg_seasons; +extern epg_object_tree_t epg_episodes; + +/* ************************************************************************** + * Load + * *************************************************************************/ + +/* + * Use for v1 databases + */ +static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats ) +{ + channel_t *ch; + epg_episode_t *ee; + epg_broadcast_t *ebc; + uint32_t ch_id = 0; + uint32_t e_start = 0; + uint32_t e_stop = 0; + uint32_t u32; + const char *title, *desc, *str; + char *uri; + int save = 0; + + /* Check key info */ + if(htsmsg_get_u32(c, "ch_id", &ch_id)) return; + if((ch = channel_find_by_identifier(ch_id)) == NULL) return; + if(htsmsg_get_u32(c, "start", &e_start)) return; + if(htsmsg_get_u32(c, "stop", &e_stop)) return; + if(!(title = htsmsg_get_str(c, "title"))) return; + + /* Create broadcast */ + save = 0; + ebc = epg_broadcast_find_by_time(ch, e_start, e_stop, 0, 1, &save); + if (!ebc) return; + if (save) stats->broadcasts.total++; + + /* Create episode */ + save = 0; + desc = htsmsg_get_str(c, "desc"); + uri = md5sum(desc ?: title); + ee = epg_episode_find_by_uri(uri, 1, &save); + free(uri); + if (!ee) return; + if (save) stats->episodes.total++; + if (title) + save |= epg_episode_set_title(ee, title); + if (desc) + save |= epg_episode_set_summary(ee, desc); + if (!htsmsg_get_u32(c, "episode", &u32)) + save |= epg_episode_set_number(ee, u32); + if (!htsmsg_get_u32(c, "part", &u32)) + save |= epg_episode_set_part(ee, u32, 0); + if (!htsmsg_get_u32(c, "season", &u32)) + ee->epnum.s_num = u32; + if ((str = htsmsg_get_str(c, "epname"))) + ee->epnum.text = strdup(str); + + /* Set episode */ + save |= epg_broadcast_set_episode(ebc, ee); +} + +/* + * Process v2 data + */ +static void _epgdb_v2_process ( htsmsg_t *m, epggrab_stats_t *stats ) +{ + int save = 0; + const char *s; + static char *sect; + + /* New section */ + if ( (s = htsmsg_get_str(m, "__section__")) ) { + if (sect) free(sect); + sect = strdup(s); + + /* Brand */ + } else if ( !strcmp(sect, "brands") ) { + if (epg_brand_deserialize(m, 1, &save)) stats->brands.total++; + + /* Season */ + } else if ( !strcmp(sect, "seasons") ) { + if (epg_season_deserialize(m, 1, &save)) stats->seasons.total++; + + /* Episode */ + } else if ( !strcmp(sect, "episodes") ) { + if (epg_episode_deserialize(m, 1, &save)) stats->episodes.total++; + + /* Broadcasts */ + } else if ( !strcmp(sect, "broadcasts") ) { + if (epg_broadcast_deserialize(m, 1, &save)) stats->broadcasts.total++; + + /* Unknown */ + } else { + tvhlog(LOG_DEBUG, "epgdb", "malformed database section [%s]", sect); + //htsmsg_print(m); + } +} + +/* + * Load data + */ +void epg_init ( void ) +{ + int fd = -1; + struct stat st; + size_t remain; + uint8_t *mem, *rp; + epggrab_stats_t stats; + int ver = EPG_DB_VERSION; + + /* Find the right file (and version) */ + while (fd < 0 && ver > 0) { + fd = hts_settings_open_file(0, "epgdb.v%d", ver); + if (fd) break; + ver--; + } + if (!fd) + fd = hts_settings_open_file(0, "epgdb"); + if ( fd < 0 ) { + tvhlog(LOG_DEBUG, "epgdb", "database does not exist"); + return; + } + + /* Map file to memory */ + if ( fstat(fd, &st) != 0 ) { + tvhlog(LOG_ERR, "epgdb", "failed to detect database size"); + return; + } + if ( !st.st_size ) { + tvhlog(LOG_DEBUG, "epgdb", "database is empty"); + return; + } + remain = st.st_size; + rp = mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if ( mem == MAP_FAILED ) { + tvhlog(LOG_ERR, "epgdb", "failed to mmap database"); + return; + } + + /* Process */ + memset(&stats, 0, sizeof(stats)); + while ( remain > 4 ) { + + /* Get message length */ + int msglen = (rp[0] << 24) | (rp[1] << 16) | (rp[2] << 8) | rp[3]; + remain -= 4; + rp += 4; + + /* Extract message */ + htsmsg_t *m = htsmsg_binary_deserialize(rp, msglen, NULL); + + /* Next */ + rp += msglen; + remain -= msglen; + + /* Skip */ + if (!m) continue; + + /* Process */ + switch (ver) { + case 2: + _epgdb_v2_process(m, &stats); + break; + default: /* v0/1 */ + _epgdb_v1_process(m, &stats); + break; + } + + /* Cleanup */ + htsmsg_destroy(m); + } + + /* Stats */ + tvhlog(LOG_INFO, "epgdb", "loaded v%d", ver); + tvhlog(LOG_INFO, "epgdb", " channels %d", stats.channels.total); + tvhlog(LOG_INFO, "epgdb", " brands %d", stats.brands.total); + tvhlog(LOG_INFO, "epgdb", " seasons %d", stats.seasons.total); + tvhlog(LOG_INFO, "epgdb", " episodes %d", stats.episodes.total); + tvhlog(LOG_INFO, "epgdb", " broadcasts %d", stats.broadcasts.total); + + /* Close file */ + munmap(mem, st.st_size); + close(fd); +} + +/* ************************************************************************** + * Save + * *************************************************************************/ + +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; + } + } else { + ret = 0; + } + if(ret) { + tvhlog(LOG_ERR, "epgdb", "failed to store epg to disk"); + close(fd); + hts_settings_remove("epgdb.v%d", EPG_DB_VERSION); + } + return ret; +} + +static int _epg_write_sect ( int fd, const char *sect ) +{ + htsmsg_t *m = htsmsg_create_map(); + htsmsg_add_str(m, "__section__", sect); + return _epg_write(fd, m); +} + +void epg_save ( void ) +{ + int fd; + epg_object_t *eo; + epg_broadcast_t *ebc; + channel_t *ch; + epggrab_stats_t stats; + + fd = hts_settings_open_file(1, "epgdb.v%d", EPG_DB_VERSION); + + memset(&stats, 0, sizeof(stats)); + if ( _epg_write_sect(fd, "brands") ) return; + RB_FOREACH(eo, &epg_brands, uri_link) { + if (_epg_write(fd, epg_brand_serialize((epg_brand_t*)eo))) return; + stats.brands.total++; + } + if ( _epg_write_sect(fd, "seasons") ) return; + RB_FOREACH(eo, &epg_seasons, uri_link) { + if (_epg_write(fd, epg_season_serialize((epg_season_t*)eo))) return; + stats.seasons.total++; + } + if ( _epg_write_sect(fd, "episodes") ) return; + RB_FOREACH(eo, &epg_episodes, uri_link) { + if (_epg_write(fd, epg_episode_serialize((epg_episode_t*)eo))) return; + stats.episodes.total++; + } + if ( _epg_write_sect(fd, "broadcasts") ) return; + RB_FOREACH(ch, &channel_name_tree, ch_name_link) { + RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) { + if (_epg_write(fd, epg_broadcast_serialize(ebc))) return; + stats.broadcasts.total++; + } + } + + /* Stats */ + tvhlog(LOG_INFO, "epgdb", "saved"); + tvhlog(LOG_INFO, "epgdb", " brands %d", stats.brands.total); + tvhlog(LOG_INFO, "epgdb", " seasons %d", stats.seasons.total); + tvhlog(LOG_INFO, "epgdb", " episodes %d", stats.episodes.total); + tvhlog(LOG_INFO, "epgdb", " broadcasts %d", stats.broadcasts.total); +}