summaryrefslogtreecommitdiff
path: root/parse.cpp
diff options
context:
space:
mode:
authorJochen Dolze <vdr@dolze.de>2010-12-29 16:54:30 +0100
committerJochen Dolze <vdr@dolze.de>2010-12-29 16:54:30 +0100
commit83cf2436ce3f2ed9ce4e0a0847b10d5144907061 (patch)
tree5cde43e0da488ac42c1b4c757a8054cfe410d588 /parse.cpp
downloadvdr-plugin-xmltv2vdr-83cf2436ce3f2ed9ce4e0a0847b10d5144907061.tar.gz
vdr-plugin-xmltv2vdr-83cf2436ce3f2ed9ce4e0a0847b10d5144907061.tar.bz2
First commit
Diffstat (limited to 'parse.cpp')
-rw-r--r--parse.cpp647
1 files changed, 647 insertions, 0 deletions
diff --git a/parse.cpp b/parse.cpp
new file mode 100644
index 0000000..747567d
--- /dev/null
+++ b/parse.cpp
@@ -0,0 +1,647 @@
+/*
+ * parse.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "parse.h"
+
+extern char *strcatrealloc(char *dest, const char *src);
+
+void cXMLTVEvent::SetTitle(const char *Title)
+{
+ title = strcpyrealloc(title, Title);
+ title = compactspace(title);
+}
+
+void cXMLTVEvent::SetOrigTitle(const char *OrigTitle)
+{
+ origtitle = strcpyrealloc(origtitle, OrigTitle);
+ origtitle = compactspace(origtitle);
+}
+
+void cXMLTVEvent::SetShortText(const char *ShortText)
+{
+ shorttext=strcpyrealloc(shorttext,ShortText);
+ shorttext=compactspace(shorttext);
+}
+
+void cXMLTVEvent::SetDescription(const char *Description)
+{
+ description = strcpyrealloc(description, Description);
+ description = compactspace(description);
+ if (description)
+ {
+ if (description[strlen(description)-1]!='\n')
+ description=strcatrealloc(description,"\n");
+ }
+}
+
+bool cXMLTVEvent::AddDescription(const char *Name, const char *Value)
+{
+ description = strcatrealloc(description,Name);
+ description = strcatrealloc(description,": ");
+ description = strcatrealloc(description,Value);
+ description = strcatrealloc(description,"\n");
+ return (description);
+}
+
+bool cXMLTVEvent::AddDescription(const char *Name, int Value)
+{
+ char *value=NULL;
+ if (asprintf(&value,"%i",Value)==-1) return false;
+ description = strcatrealloc(description,Name);
+ description = strcatrealloc(description,": ");
+ description = strcatrealloc(description,value);
+ description = strcatrealloc(description,"\n");
+ free(value);
+ return (description);
+}
+
+void cXMLTVEvent::SetCountry(const char *Country)
+{
+ country=strcpyrealloc(country, Country);
+ country=compactspace(country);
+}
+
+void cXMLTVEvent::SetReview(const char *Review)
+{
+ review=strcpyrealloc(review, Review);
+ review=compactspace(review);
+}
+
+void cXMLTVEvent::SetRating(const char *System, const char *Rating)
+{
+ system=strcpyrealloc(system, System);
+ system=compactspace(system);
+
+ rating=strcpyrealloc(rating, Rating);
+ rating=compactspace(rating);
+}
+
+void cXMLTVEvent::Clear()
+{
+ if (title)
+ {
+ free(title);
+ title=NULL;
+ }
+ if (shorttext)
+ {
+ free(shorttext);
+ shorttext=NULL;
+ }
+ if (description)
+ {
+ free(description);
+ description=NULL;
+ }
+ if (country)
+ {
+ free(country);
+ country=NULL;
+ }
+ if (system)
+ {
+ free(system);
+ system=NULL;
+ }
+ if (review)
+ {
+ free(review);
+ review=NULL;
+ }
+ if (rating)
+ {
+ free(rating);
+ rating=NULL;
+ }
+ if (origtitle)
+ {
+ free(origtitle);
+ origtitle=NULL;
+ }
+ year=0;
+ vps= (time_t) 0;
+ starttime = 0;
+ duration = 0;
+ eventid=0;
+}
+
+cXMLTVEvent::cXMLTVEvent()
+{
+ title=NULL;
+ shorttext=NULL;
+ description=NULL;
+ country=NULL;
+ system=NULL;
+ rating=NULL;
+ review=NULL;
+ origtitle=NULL;
+ Clear();
+}
+
+cXMLTVEvent::~cXMLTVEvent()
+{
+ Clear();
+}
+
+// -------------------------------------------------------
+
+time_t cParse::ConvertXMLTVTime2UnixTime(char *xmltvtime)
+{
+ time_t offset=0;
+ if (!xmltvtime) return (time_t) 0;
+ char *withtz=strchr(xmltvtime,' ');
+ int len;
+ if (withtz)
+ {
+ len=strlen(xmltvtime)-(withtz-xmltvtime)-1;
+ *withtz=':';
+ if ((withtz[1]=='+') || (withtz[1]=='-'))
+ {
+ if (len==5)
+ {
+ int val=atoi(&withtz[1]);
+ int h=val/100;
+ int m=val-(h*100);
+ offset=h*3600+m*60;
+ setenv("TZ",":UTC",1);
+ }
+ else
+ {
+ setenv("TZ",":UTC",1);
+ }
+ }
+ else
+ {
+ if (len>2)
+ {
+ setenv("TZ",withtz,1);
+ }
+ else
+ {
+ setenv("TZ",":UTC",1);
+ }
+ }
+ }
+ else
+ {
+ withtz=&xmltvtime[strlen(xmltvtime)];
+ setenv("TZ",":UTC",1);
+ }
+ tzset();
+
+ len=withtz-xmltvtime;
+ if (len<4)
+ {
+ unsetenv("TZ");
+ tzset();
+ return (time_t) 0;
+ }
+ len-=2;
+ char fmt[]="%Y%m%d%H%M%S";
+ fmt[len]=0;
+
+ struct tm tm;
+ memset(&tm,0,sizeof(tm));
+ if (!strptime(xmltvtime,fmt,&tm))
+ {
+ unsetenv("TZ");
+ tzset();
+ return (time_t) 0;
+ }
+ if (tm.tm_mday==0) tm.tm_mday=1;
+ time_t ret=mktime(&tm);
+ ret-=offset;
+ unsetenv("TZ");
+ tzset();
+ return ret;
+}
+
+cEvent *cParse::SearchEvent(cSchedule* schedule, cXMLTVEvent *xevent)
+{
+ if (!xevent) return NULL;
+ if (!xevent->Duration()) return NULL;
+ if (!xevent->StartTime()) return NULL;
+ if (!xevent->Title()) return NULL;
+ cEvent *f=NULL;
+
+ time_t start=xevent->StartTime();
+ if (!xevent->EventID())
+ {
+ xevent->SetEventID(DoSum((u_long) start,xevent->Title(),strlen(xevent->Title())));
+ }
+
+ // try to find an event,
+ // 1st with our own EventID
+ if (xevent->EventID()) f=(cEvent *) schedule->GetEvent((tEventID) xevent->EventID());
+ if (f) return f;
+ // 2nd with StartTime
+ f=(cEvent *) schedule->GetEvent((tEventID) 0,start);
+ if (f) return f;
+ // 3rd with StartTime +/- WaitTime
+ int maxdiff=INT_MAX;
+ int eventTimeDiff=xevent->Duration()/4;
+ if (eventTimeDiff<600) eventTimeDiff=600;
+
+ for (cEvent *p = schedule->Events()->First(); p; p = schedule->Events()->Next(p))
+ {
+ if (!strcmp(p->Title(),xevent->Title()))
+ {
+ // found event with same title
+ int diff=abs((int) difftime(p->StartTime(),start));
+ if (diff<=eventTimeDiff)
+ {
+ if (diff<=maxdiff)
+ {
+ f=p;
+ maxdiff=diff;
+ }
+ }
+ }
+ }
+ return f;
+}
+
+bool cParse::PutEvent(cSchedule* schedule, cEvent *event, cXMLTVEvent *xevent, cEPGMapping *map)
+{
+ if (!schedule) return false;
+ if (!xevent) return false;
+ if (!map) return false;
+
+ time_t start;
+ if (!event)
+ {
+ if ((map->Flags() & OPT_APPEND)==OPT_APPEND)
+ {
+ start=xevent->StartTime();
+ event=new cEvent(xevent->EventID());
+ if (!event) return false;
+ event->SetStartTime(start);
+ event->SetDuration(xevent->Duration());
+ event->SetTitle(xevent->Title());
+ event->SetShortText(xevent->ShortText());
+ event->SetDescription(xevent->Description());
+ event->SetVersion(0);
+ schedule->AddEvent(event);
+ dsyslog("xmltv2vdr: '%s' adding event '%s' @%s",name,xevent->Title(),ctime(&start));
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (event->TableID()==0) return true;
+ start=event->StartTime();
+ dsyslog("xmltv2vdr: '%s' changing event '%s' @%s",name,event->Title(),ctime(&start));
+ }
+ if ((map->Flags() & USE_SHORTTEXT)==USE_SHORTTEXT)
+ {
+ event->SetShortText(xevent->ShortText());
+ }
+ if ((map->Flags() & USE_LONGTEXT)==USE_LONGTEXT)
+ {
+ event->SetDescription(xevent->Description());
+ }
+ else
+ {
+ xevent->SetDescription(event->Description());
+ }
+ bool addExt=false;
+ if ((map->Flags() & USE_COUNTRYDATE)==USE_COUNTRYDATE)
+ {
+ if (xevent->Country())
+ {
+ cTEXTMapping *text=TEXTMapping("country");
+ if (text) addExt=xevent->AddDescription(text->Value(),xevent->Country());
+ }
+
+ if (xevent->Year())
+ {
+ cTEXTMapping *text=TEXTMapping("date");
+ if (text) addExt=xevent->AddDescription(text->Value(),xevent->Year());
+ }
+ }
+ if (((map->Flags() & USE_ORIGTITLE)==USE_ORIGTITLE) && (xevent->OrigTitle()))
+ {
+ cTEXTMapping *text;
+ text=TEXTMapping("originaltitle");
+ if (text) addExt=xevent->AddDescription(text->Value(),xevent->OrigTitle());
+ }
+ if (((map->Flags() & USE_RATING)==USE_RATING) && (xevent->Rating()) && (xevent->RatingSystem()))
+ {
+ addExt=xevent->AddDescription(xevent->RatingSystem(),xevent->Rating());
+ }
+ if (((map->Flags() & USE_REVIEW)==USE_REVIEW) && (xevent->Review()))
+ {
+ cTEXTMapping *text;
+ text=TEXTMapping("review");
+ if (text) addExt=xevent->AddDescription(text->Value(),xevent->Review());
+ }
+ if (addExt) event->SetDescription(xevent->Description());
+ event->SetTableID(0); // prevent EIT EPG to update this event
+ return true;
+}
+
+u_long cParse::DoSum(u_long sum, const char *buf, int nBytes)
+{
+ int nleft=nBytes;
+ u_short *w = (u_short*)buf;
+
+ while (nleft > 1)
+ {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1)
+ {
+ u_short answer = 0;
+ *(u_char*)(&answer) = *(u_char*)w;
+ sum += answer;
+ }
+ return sum;
+}
+
+bool cParse::FetchEvent(xmlNodePtr enode)
+{
+ char *slang=getenv("LANG");
+ xmlNodePtr node=enode->xmlChildrenNode;
+ while (node)
+ {
+ if (node->type==XML_ELEMENT_NODE)
+ {
+ if ((!xmlStrcasecmp(node->name, (const xmlChar *) "title")))
+ {
+ xmlChar *lang=xmlGetProp(node,(const xmlChar *) "lang");
+ if (lang && slang && !xmlStrncasecmp(lang, (const xmlChar *) slang,2))
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetTitle(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ xmlFree(lang);
+ }
+ else
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetOrigTitle(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "sub-title")))
+ {
+ // what to do with attribute lang?
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetShortText(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "desc")))
+ {
+ // what to do with attribute lang?
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetDescription(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "country")))
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetCountry(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "date")))
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetYear(atoi((const char *) content));
+ xmlFree(content);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "category")))
+ {
+ // attribute lang
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "credits")))
+ {
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "rating")))
+ {
+ xmlChar *system=xmlGetProp(node,(const xmlChar *) "system");
+ if (system)
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ const char *crating=strdup(conv->Convert((const char *) content));
+ const char *csystem=strdup(conv->Convert((const char *) system));
+ xevent.SetRating(csystem,crating);
+ if (crating) free((void *) crating);
+ if (csystem) free((void *) csystem);
+ xmlFree(content);
+ }
+ xmlFree(system);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "review")))
+ {
+ xmlChar *type=xmlGetProp(node,(const xmlChar *) "type");
+ if (type && !xmlStrcasecmp(type, (const xmlChar *) "text"))
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetReview(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ xmlFree(type);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "video")))
+ {
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "audio")))
+ {
+ }
+ else
+ {
+ esyslog("xmltv2vdr: '%s' unknown element %s, please report!",name,node->name);
+ }
+ }
+ node=node->next;
+ }
+ return true;
+}
+
+cTEXTMapping *cParse::TEXTMapping(const char *Name)
+{
+ if (!texts->Count()) return NULL;
+ for (cTEXTMapping *textmap=texts->First(); textmap; textmap=texts->Next(textmap))
+ {
+ if (!strcmp(textmap->Name(),Name)) return textmap;
+ }
+ return NULL;
+}
+
+cEPGMapping *cParse::EPGMapping(const char *ChannelName)
+{
+ if (!maps->Count()) return NULL;
+ for (cEPGMapping *map=maps->First(); map; map=maps->Next(map))
+ {
+ if (!strcmp(map->ChannelName(),ChannelName)) return map;
+ }
+ return NULL;
+}
+
+bool cParse::Process(char *buffer, int bufsize)
+{
+ if (!buffer) return false;
+ if (!bufsize) return false;
+
+ xmlDocPtr xmltv;
+ xmltv=xmlReadMemory(buffer,bufsize,NULL,NULL,0);
+ if (!xmltv) return false;
+
+ xmlNodePtr rootnode=xmlDocGetRootElement(xmltv);
+ if (!rootnode)
+ {
+ xmlFreeDoc(xmltv);
+ return false;
+ }
+
+ time_t begin=time(NULL);
+ xmlNodePtr node=rootnode->xmlChildrenNode;
+ while (node)
+ {
+ if (node->type==XML_ELEMENT_NODE)
+ {
+ if ((!xmlStrcasecmp(node->name, (const xmlChar *) "programme")))
+ {
+ xmlChar *channelid=xmlGetProp(node,(const xmlChar *) "channelid");
+ cEPGMapping *map=NULL;
+ if (channelid && (map=EPGMapping((const char *) channelid)))
+ {
+ time_t end=begin+86000*map->Days();
+ xmlChar *start,*stop;
+ time_t starttime=(time_t) 0;
+ time_t stoptime=(time_t) 0;
+ start=xmlGetProp(node,(const xmlChar *) "start");
+ if (start)
+ {
+ starttime=ConvertXMLTVTime2UnixTime((char *) start);
+ if (starttime)
+ {
+ stop=xmlGetProp(node,(const xmlChar *) "stop");
+ if (stop)
+ {
+ stoptime=ConvertXMLTVTime2UnixTime((char *) stop);
+ xmlFree(stop);
+ }
+ }
+ xmlFree(start);
+ }
+
+ if (starttime && (starttime>begin) && (starttime<end))
+ {
+ xevent.Clear();
+
+ xmlChar *eventid;
+ eventid=xmlGetProp(node,(const xmlChar *) "eventid");
+ if (eventid)
+ {
+ xevent.SetEventID(atoi((const char *) eventid));
+ xmlFree(eventid);
+ }
+
+ xmlChar *vpsstart=xmlGetProp(node,(const xmlChar *) "vps-start");
+ if (vpsstart)
+ {
+ time_t vps=ConvertXMLTVTime2UnixTime((char *) vpsstart);
+ xevent.SetVps(vps);
+ xmlFree(vpsstart);
+ }
+
+ xevent.SetStartTime(starttime);
+ if (stoptime) xevent.SetDuration(stoptime-starttime);
+ FetchEvent(node);
+
+ cSchedulesLock *schedulesLock = new cSchedulesLock(true,2000); // to be safe ;)
+ const cSchedules *schedules = cSchedules::Schedules(*schedulesLock);
+ if (schedules)
+ {
+ for (int i=0; i<map->NumChannelIDs(); i++)
+ {
+ bool addevents=false;
+ if ((map->Flags() & OPT_APPEND)==OPT_APPEND) addevents=true;
+
+ cChannel *channel=Channels.GetByChannelID(map->ChannelIDs()[i]);
+ cSchedule* schedule = (cSchedule *) schedules->GetSchedule(channel,addevents);
+ if (schedule)
+ {
+ cEvent *event=NULL;
+ if ((event=SearchEvent(schedule,&xevent)))
+ {
+ PutEvent(schedule,event,&xevent,map);
+ }
+ else
+ {
+ if (addevents) PutEvent(schedule,event,&xevent,map);
+ }
+ }
+ }
+ }
+ delete schedulesLock;
+ }
+ xmlFree(channelid);
+ }
+ }
+ }
+ node=node->next;
+ }
+ xmlFreeDoc(xmltv);
+ return true;
+}
+
+cParse::cParse(const char *Name, cEPGMappings *Maps, cTEXTMappings *Texts)
+{
+#if VDRVERSNUM < 10701 || defined(__FreeBSD__)
+ conv = new cCharSetConv("UTF-8",cCharSetConv::SystemCharacterTable() ?
+ cCharSetConv::SystemCharacterTable() : "UTF-8");
+#else
+ conv = new cCharSetConv("UTF-8",NULL);
+#endif
+ name=strdup(Name);
+ maps=Maps;
+ texts=Texts;
+}
+
+cParse::~cParse()
+{
+ delete conv;
+ free(name);
+}