diff options
Diffstat (limited to 'import.cpp')
-rw-r--r-- | import.cpp | 1222 |
1 files changed, 1222 insertions, 0 deletions
diff --git a/import.cpp b/import.cpp new file mode 100644 index 0000000..0bd1d00 --- /dev/null +++ b/import.cpp @@ -0,0 +1,1222 @@ +/* + * import.h: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <sqlite3.h> +#include <ctype.h> +#include <time.h> +#include <locale.h> +#include <langinfo.h> +#include <pcrecpp.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#include <vdr/channels.h> + +#include "xmltv2vdr.h" +#include "import.h" +#include "event.h" + +extern char *strcatrealloc(char *, const char*); + +struct cImport::split cImport::split(char *in, char delim) +{ + struct split sp; + sp.count=1; + sp.pointers[0]=in; + while (*++in) + { + if (*in==delim) + { + *in=0; + sp.pointers[sp.count++]=in+1; + } + } + return sp; +} + +char *cImport::RemoveNonASCII(const char *src) +{ + if (!src) return NULL; + int len=strlen(src); + if (!len) return NULL; + char *dst=(char *) malloc(len+1); + if (!dst) return NULL; + char *tmp=dst; + bool lspc=false; + while (*src) + { + // 0x20,0x30-0x39,0x41-0x5A,0x61-0x7A + if ((*src==0x20) && (!lspc)) + { + *tmp++=0x20; + lspc=true; + } + if (*src==':') + { + *tmp++=0x20; + lspc=true; + } + if ((*src>=0x30) && (*src<=0x39)) + { + *tmp++=*src; + lspc=false; + } + if ((*src>=0x41) && (*src<=0x5A)) + { + *tmp++=tolower(*src); + lspc=false; + } + if ((*src>=0x61) && (*src<=0x7A)) + { + *tmp++=*src; + lspc=false; + } + src++; + } + *tmp=0; + return dst; +} + +cEvent *cImport::SearchVDREvent(cSchedule* schedule, cXMLTVEvent *xevent) +{ + cEvent *f=NULL; + + // try to find an event, + // 1st with our own EventID + if (xevent->EITEventID()) f=(cEvent *) schedule->GetEvent(xevent->EITEventID()); + if (f) return f; + + if (xevent->EventID() && !xevent->Mixing()) f=(cEvent *) schedule->GetEvent(xevent->EventID()); + if (f) return f; + + // 2nd with StartTime + f=(cEvent *) schedule->GetEvent((tEventID) 0,xevent->StartTime()); + if (f) + { + if (!strcmp(f->Title(),conv->Convert(xevent->Title()))) + { + return f; + } + } + // 3rd with StartTime +/- WaitTime + int maxdiff=INT_MAX; + int eventTimeDiff=0; + if (xevent->Duration()) eventTimeDiff=xevent->Duration()/4; + if (eventTimeDiff<780) eventTimeDiff=780; + + for (cEvent *p = schedule->Events()->First(); p; p = schedule->Events()->Next(p)) + { + int diff=abs((int) difftime(p->StartTime(),xevent->StartTime())); + if (diff<=eventTimeDiff) + { + // found event with exact the same title + if (!strcmp(p->Title(),conv->Convert(xevent->Title()))) + { + if (diff<=maxdiff) + { + f=p; + maxdiff=diff; + } + } + else + { + if (f) continue; // we already have an event! + // cut both titles into pieces and check + // if we have at least one match with + // minimum length of 4 characters + + // first remove all non ascii characters + // we just want the following codes + // 0x20,0x30-0x39,0x41-0x5A,0x61-0x7A + int wfound=0; + char *s1=RemoveNonASCII(p->Title()); + char *s2=RemoveNonASCII(conv->Convert(xevent->Title())); + if (s1 && s2) + { + if (!strcmp(s1,s2)) + { + wfound++; + } + else + { + struct split w1 = split(s1,' '); + struct split w2 = split(s2,' '); + if ((w1.count) && (w2.count)) + { + for (int i1=0; i1<w1.count; i1++) + { + for (int i2=0; i2<w2.count; i2++) + { + if (!strcmp(w1.pointers[i1],w2.pointers[i2])) + { + if (strlen(w1.pointers[i1])>3) wfound++; + } + } + } + } + } + } + if (s1) free(s1); + if (s2) free(s2); + + if (wfound) + { + if (diff<=maxdiff) + { + if (p->TableID()!=0) + source->Dlog("found '%s' for '%s'",p->Title(),conv->Convert(xevent->Title())); + f=p; + maxdiff=diff; + } + } + } + } + } + return f; +} + +cEvent *cImport::GetEventBefore(cSchedule* schedule, time_t start) +{ + if (!schedule) return NULL; + if (!schedule->Events()) return NULL; + if (!schedule->Events()->Count()) return NULL; + cEvent *last=schedule->Events()->Last(); + if ((last) && (last->StartTime()<start)) return last; + for (cEvent *p=schedule->Events()->First(); p; p=schedule->Events()->Next(p)) + { + if (p->StartTime()>start) + { + return (cEvent *) p->Prev(); + } + } + if (last) return last; + return NULL; +} + +char *cImport::RemoveLastCharFromDescription(char *description) +{ + if (description) + { + int len=strlen(description); + if (!len) return description; + description[len-1]=0; + } + return description; +} + +char *cImport::Add2Description(char *description, const char *Value) +{ + description = strcatrealloc(description,Value); + return description; +} + +char *cImport::Add2Description(char *description, const char *Name, const char *Value) +{ + description = strcatrealloc(description,Name); + description = strcatrealloc(description,": "); + description = strcatrealloc(description,Value); + description = strcatrealloc(description,"\n"); + return description; +} + +char *cImport::Add2Description(char *description, 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; +} + +char *cImport::AddEOT2Description(char *description) +{ + const char nbsp[]={0xc2,0xa0,0}; + description=strcatrealloc(description,nbsp); + return description; +} + +bool cImport::WasChanged(cEvent* event) +{ + if (!event) return false; + if (!event->Description()) return false; + int len=strlen(event->Description()); + if ((uchar)(event->Description()[len-1])==0xA0) return true; + return false; +} + +bool cImport::PutEvent(cEPGSource *source, sqlite3 *db, cSchedule* schedule, + cEvent *event, cXMLTVEvent *xevent,int Flags, int Option) +{ + if (!source) return false; + if (!schedule) return false; + if (!xevent) return false; + + struct tm tm; + char from[80]; + char till[80]; + time_t start,end; + + bool changed=false; + bool append=false; + if ((Flags & OPT_APPEND)==OPT_APPEND) append=true; + + if (append && !event) + { + start=xevent->StartTime(); + end=start+xevent->Duration(); + + /* checking the "space" for our new event */ + cEvent *prev=GetEventBefore(schedule,start); + if (prev) + { + if (cEvent *next=(cEvent *) prev->Next()) + { + if (prev->EndTime()==next->StartTime()) + { + localtime_r(&start,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&end,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Elog("cannot add '%s'@%s-%s",xevent->Title(),from,till); + + time_t pstart=prev->StartTime(); + time_t pstop=prev->EndTime(); + localtime_r(&pstart,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&pstop,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Elog("found '%s'@%s-%s",prev->Title(),from,till); + + time_t nstart=next->StartTime(); + time_t nstop=next->EndTime(); + localtime_r(&nstart,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&nstop,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Elog("found '%s'@%s-%s",next->Title(),from,till); + return false; + } + + if (end>next->StartTime()) + { + int diff=(int) difftime(prev->EndTime(),start); + if (diff>300) + { + + localtime_r(&start,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&end,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Elog("cannot add '%s'@%s-%s",xevent->Title(),from,till); + + time_t nstart=next->StartTime(); + time_t nstop=next->EndTime(); + localtime_r(&nstart,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&nstop,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Elog("found '%s'@%s-%s",next->Title(),from,till); + return false; + } + else + { + xevent->SetDuration(xevent->Duration()-diff); + } + } + } + + if (prev->EndTime()>start) + { + int diff=(int) difftime(prev->EndTime(),start); + if (diff>300) + { + localtime_r(&start,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&end,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Elog("cannot add '%s'@%s-%s",xevent->Title(),from,till); + + time_t pstart=prev->StartTime(); + time_t pstop=prev->EndTime(); + localtime_r(&pstart,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&pstop,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Elog("found '%s'@%s-%s",prev->Title(),from,till); + return false; + } + else + { + prev->SetDuration(prev->Duration()-diff); + } + } + + if (!xevent->Duration()) + { + if (!prev->Duration()) + { + prev->SetDuration(start-prev->StartTime()); + } + } + } + /* add event */ + event=new cEvent(xevent->EventID()); + if (!event) return false; + event->SetStartTime(start); + event->SetDuration(xevent->Duration()); + event->SetTitle(xevent->Title()); + event->SetVersion(0); + event->SetTableID(0); + schedule->AddEvent(event); + schedule->Sort(); + localtime_r(&start,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&end,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Dlog("adding '%s'@%s-%s",xevent->Title(),from,till); + } + else + { + append=false; + } + + if (!event) return false; + + if (((Flags & USE_SHORTTEXT)==USE_SHORTTEXT) || (append)) + { + if (xevent->ShortText() && (strlen(xevent->ShortText())>0)) + { + if (!strcasecmp(xevent->ShortText(),event->Title())) + { + source->Dlog("title and subtitle equal, clearing subtitle"); + event->SetShortText(""); + changed=true; + } + else + { + const char *dp=conv->Convert(xevent->ShortText()); + if (!event->ShortText() || strcmp(event->ShortText(),dp)) + { + event->SetShortText(dp); + changed=true; + } + } + } + } + + if (Option!=IMPORT_SHORTTEXT) + { + char *description=NULL; + if (((Flags & USE_LONGTEXT)==USE_LONGTEXT) || ((Flags & OPT_APPEND)==OPT_APPEND)) + { + if (xevent->Description() && (strlen(xevent->Description())>0)) + { + description=strdup(xevent->Description()); + } + } + + if (!description && xevent->EITDescription() && (strlen(xevent->EITDescription())>0)) + { + description=strdup(xevent->EITDescription()); + } + + if (!description && event->Description() && (strlen(event->Description())>0)) + { + if (WasChanged(event)) return true; + UpdateXMLTVEvent(source->EPGFile(),db,source->Name(),xevent->EventID(),event->EventID(), + event->Description()); + description=strdup(event->Description()); + } + + if (description) description=Add2Description(description,"\n"); + + if ((Flags & USE_CREDITS)==USE_CREDITS) + { + cXMLTVStringList *credits=xevent->Credits(); + if (credits->Size()) + { + cTEXTMapping *oldtext=NULL; + for (int i=0; i<credits->Size(); i++) + { + char *ctype=strdup((*credits)[i]); + if (ctype) + { + char *cval=strchr(ctype,'|'); + if (cval) + { + *cval=0; + cval++; + bool add=true; + if (((Flags & CREDITS_ACTORS)!=CREDITS_ACTORS) && + (!strcasecmp(ctype,"actor"))) add=false; + if (((Flags & CREDITS_DIRECTORS)!=CREDITS_DIRECTORS) && + (!strcasecmp(ctype,"director"))) add=false; + if (((Flags & CREDITS_OTHERS)!=CREDITS_OTHERS) && + (add) && (strcasecmp(ctype,"actor")) && + (strcasecmp(ctype,"director"))) add=false; + if (add) + { + cTEXTMapping *text=texts->GetMap(ctype); + if ((Flags & CREDITS_LIST)==CREDITS_LIST) + { + if (oldtext!=text) + { + if (oldtext) + { + description=RemoveLastCharFromDescription(description); + description=RemoveLastCharFromDescription(description); + description=Add2Description(description,"\n"); + } + description=Add2Description(description,text->Value()); + description=Add2Description(description,": "); + } + description=Add2Description(description,cval); + description=Add2Description(description,", "); + } + else + { + if (text) + { + description=Add2Description(description,text->Value(),cval); + } + } + oldtext=text; + } + } + free(ctype); + } + } + if ((oldtext) && ((Flags & CREDITS_LIST)==CREDITS_LIST)) + { + description=RemoveLastCharFromDescription(description); + description=RemoveLastCharFromDescription(description); + description=Add2Description(description,"\n"); + } + } + } + + if ((Flags & USE_COUNTRYDATE)==USE_COUNTRYDATE) + { + if (xevent->Country()) + { + cTEXTMapping *text=texts->GetMap("country"); + if (text) description=Add2Description(description,text->Value(),xevent->Country()); + } + + if (xevent->Year()) + { + cTEXTMapping *text=texts->GetMap("year"); + if (text) description=Add2Description(description,text->Value(),xevent->Year()); + } + } + if (((Flags & USE_ORIGTITLE)==USE_ORIGTITLE) && (xevent->OrigTitle())) + { + cTEXTMapping *text=texts->GetMap("originaltitle"); + if (text) description=Add2Description(description,text->Value(),xevent->OrigTitle()); + } + if (((Flags & USE_CATEGORIES)==USE_CATEGORIES) && (xevent->Category()->Size())) + { + cTEXTMapping *text=texts->GetMap("category"); + if (text) + { + cXMLTVStringList *categories=xevent->Category(); + description=Add2Description(description,text->Value(),(*categories)[0]); + for (int i=1; i<categories->Size(); i++) + { + // prevent duplicates + if (strcasecmp((*categories)[i],(*categories)[i-1])) + description=Add2Description(description,text->Value(),(*categories)[i]); + } + } + } + + if (((Flags & USE_VIDEO)==USE_VIDEO) && (xevent->Video()->Size())) + { + cTEXTMapping *text=texts->GetMap("video"); + if (text) + { + description=Add2Description(description,text->Value()); + description=Add2Description(description,": "); + cXMLTVStringList *video=xevent->Video(); + for (int i=0; i<video->Size(); i++) + { + char *vtype=strdup((*video)[i]); + if (vtype) + { + char *vval=strchr(vtype,'|'); + if (vval) + { + *vval=0; + vval++; + + if (i) + { + description=Add2Description(description,", "); + } + + if (!strcasecmp(vtype,"colour")) + { + if (!strcasecmp(vval,"no")) + { + cTEXTMapping *text=texts->GetMap("blacknwhite"); + description=Add2Description(description,text->Value()); + } + } + else + { + description=Add2Description(description,vval); + } + } + free(vtype); + } + } + description=Add2Description(description,"\n"); + } + } + + if ((Flags & USE_AUDIO)==USE_AUDIO) + { + if (xevent->Audio()) + { + cTEXTMapping *text=texts->GetMap("audio"); + if (text) + { + description=Add2Description(description,text->Value()); + description=Add2Description(description,": "); + + if ((!strcasecmp(xevent->Audio(),"mono")) || (!strcasecmp(xevent->Audio(),"stereo"))) + { + description=Add2Description(description,xevent->Audio()); + description=Add2Description(description,"\n"); + } + else + { + cTEXTMapping *text=texts->GetMap(xevent->Audio()); + if (text) + { + description=Add2Description(description,text->Value()); + description=Add2Description(description,"\n"); + } + } + } + } + } + if ((Flags & USE_SEASON)==USE_SEASON) + { + if (xevent->Season()) + { + cTEXTMapping *text=texts->GetMap("season"); + if (text) description=Add2Description(description,text->Value(),xevent->Season()); + } + + if (xevent->Episode()) + { + cTEXTMapping *text=texts->GetMap("episode"); + if (text) description=Add2Description(description,text->Value(),xevent->Episode()); + } + } + + if (((Flags & USE_RATING)==USE_RATING) && (xevent->Rating()->Size())) + { + cXMLTVStringList *rating=xevent->Rating(); + for (int i=0; i<rating->Size(); i++) + { + char *rtype=strdup((*rating)[i]); + if (rtype) + { + char *rval=strchr(rtype,'|'); + if (rval) + { + *rval=0; + rval++; + + description=Add2Description(description,rtype); + description=Add2Description(description,": "); + description=Add2Description(description,rval); + description=Add2Description(description,"\n"); + } + free(rtype); + } + } + } + + if (((Flags & USE_STARRATING)==USE_STARRATING) && (xevent->StarRating()->Size())) + { + cTEXTMapping *text=texts->GetMap("starrating"); + if (text) + { + description=Add2Description(description,text->Value()); + description=Add2Description(description,": "); + cXMLTVStringList *starrating=xevent->StarRating(); + for (int i=0; i<starrating->Size(); i++) + { + char *rtype=strdup((*starrating)[i]); + if (rtype) + { + char *rval=strchr(rtype,'|'); + if (rval) + { + *rval=0; + rval++; + + if (i) + { + description=Add2Description(description,", "); + } + if (strcasecmp(rtype,"*")) + { + description=Add2Description(description,rtype); + description=Add2Description(description," "); + } + description=Add2Description(description,rval); + } + free(rtype); + } + } + description=Add2Description(description,"\n"); + } + } + + if (((Flags & USE_REVIEW)==USE_REVIEW) && (xevent->Review()->Size())) + { + cTEXTMapping *text=texts->GetMap("review"); + if (text) + { + cXMLTVStringList *review=xevent->Review(); + for (int i=0; i<review->Size(); i++) + { + description=Add2Description(description,text->Value(),(*review)[i]); + } + } + } + + if (description) + { + description=RemoveLastCharFromDescription(description); + description=AddEOT2Description(description); + const char *dp=conv->Convert(description); + if (!event->Description() || strcmp(event->Description(),dp)) + { + event->SetDescription(dp); + changed=true; + } + free(description); + } + } +#if VDRVERSNUM < 10726 && (!EPGHANDLER) + event->SetTableID(0); // prevent EIT EPG to update this event +#endif + if ((!append) && (changed)) + { + start=event->StartTime(); + end=event->EndTime(); + localtime_r(&start,&tm); + strftime(from,sizeof(from)-1,"%b %d %H:%M",&tm); + localtime_r(&end,&tm); + strftime(till,sizeof(till)-1,"%b %d %H:%M",&tm); + source->Dlog("changing %s'%s'@%s-%s",xevent->EITDescription() ? "old " : "", + event->Title(),from,till); + } + return true; +} + +bool cImport::FetchXMLTVEvent(sqlite3_stmt *stmt, cXMLTVEvent *xevent) +{ + if (!stmt) return false; + if (!xevent) return false; + xevent->Clear(); + int cols=sqlite3_column_count(stmt); + for (int col=0; col<cols; col++) + { + switch (col) + { + case 0: + xevent->SetChannelID((const char *) sqlite3_column_text(stmt,col)); + break; + case 1: + xevent->SetEventID(sqlite3_column_int(stmt,col)); + break; + case 2: + xevent->SetStartTime(sqlite3_column_int(stmt,col)); + break; + case 3: + xevent->SetDuration(sqlite3_column_int(stmt,col)); + break; + case 4: + xevent->SetTitle((const char *) sqlite3_column_text(stmt,col)); + break; + case 5: + xevent->SetOrigTitle((const char *) sqlite3_column_text(stmt,col)); + break; + case 6: + xevent->SetShortText((const char *) sqlite3_column_text(stmt,col)); + break; + case 7: + xevent->SetDescription((const char *) sqlite3_column_text(stmt,col)); + break; + case 8: + xevent->SetCountry((const char *) sqlite3_column_text(stmt,col)); + break; + case 9: + xevent->SetYear(sqlite3_column_int(stmt,col)); + break; + case 10: + xevent->SetCredits((const char *) sqlite3_column_text(stmt,col)); + break; + case 11: + xevent->SetCategory((const char *) sqlite3_column_text(stmt,col)); + break; + case 12: + xevent->SetReview((const char *) sqlite3_column_text(stmt,col)); + break; + case 13: + xevent->SetRating((const char *) sqlite3_column_text(stmt,col)); + break; + case 14: + xevent->SetStarRating((const char *) sqlite3_column_text(stmt,col)); + break; + case 15: + xevent->SetVideo((const char *) sqlite3_column_text(stmt,col)); + break; + case 16: + xevent->SetAudio((const char *) sqlite3_column_text(stmt,col)); + break; + case 17: + xevent->SetSeason(sqlite3_column_int(stmt,col)); + break; + case 18: + xevent->SetEpisode(sqlite3_column_int(stmt,col)); + break; + case 19: + if (sqlite3_column_int(stmt,col)==1) xevent->SetMixing(); + break; + case 20: // source + xevent->SetSource((const char *) sqlite3_column_text(stmt,col)); + break; + case 21: // eiteventid + xevent->SetEITEventID(sqlite3_column_int(stmt,col)); + break; + case 22: // eitdescription + xevent->SetEITDescription((const char *) sqlite3_column_text(stmt,col)); + break; + } + } + return true; +} + +cXMLTVEvent *cImport::PrepareAndReturn(sqlite3 *db, char *sql, sqlite3_stmt *stmt) +{ + if (sqlite3_prepare_v2(db,sql,strlen(sql),&stmt,NULL)!=SQLITE_OK) + { + esyslog("epghander: failed to prepare %s",sql); + free(sql); + return NULL; + } + + cXMLTVEvent *xevent=NULL; + if (sqlite3_step(stmt)==SQLITE_ROW) + { + xevent = new cXMLTVEvent(); + FetchXMLTVEvent(stmt,xevent); + } + sqlite3_finalize(stmt); + free(sql); + return xevent; +} + +cXMLTVEvent *cImport::AddXMLTVEvent(const char *EPGFile, const char *ChannelID, const cEvent *Event, + const char *EITDescription) +{ + struct passwd pwd,*pwdbuf; + char buf[1024],*epdir=NULL; + iconv_t conv=(iconv_t) -1; + getpwuid_r(getuid(),&pwd,buf,sizeof(buf),&pwdbuf); + if (pwdbuf) + { + if (asprintf(&epdir,"%s/.eplists/lists",pwdbuf->pw_dir)!=-1) + { + if (access(epdir,R_OK)) + { + free(epdir); + epdir=NULL; + } + else + { + conv=iconv_open("US-ASCII//TRANSLIT","UTF-8"); + } + } + else + { + epdir=NULL; + } + } + if (!epdir) return NULL; + if (conv==(iconv_t) -1) + { + free(epdir); + return NULL; + } + + int season,episode; + if (!cParse::FetchSeasonEpisode(conv,epdir,Event->Title(), + Event->ShortText(),season,episode)) + { + free(epdir); + iconv_close(conv); + return NULL; + } + + sqlite3 *db=NULL; + if (sqlite3_open_v2(EPGFile,&db,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) + { + esyslog("epghandler: failed to open or create %s",EPGFile); + free(epdir); + iconv_close(conv); + return NULL; + } + + cXMLTVEvent *xevent = new cXMLTVEvent(); + if (!xevent) + { + sqlite3_close(db); + esyslog("epghandler: out of memory"); + free(epdir); + iconv_close(conv); + return NULL; + } + + xevent->SetTitle(Event->Title()); + xevent->SetStartTime(Event->StartTime()); + xevent->SetDuration(Event->Duration()); + xevent->SetShortText(Event->ShortText()); + xevent->SetEventID(Event->EventID()); + xevent->SetEITEventID(Event->EventID()); + xevent->SetDescription(EITDescription); + xevent->SetEITDescription(EITDescription); + xevent->SetSeason(season); + xevent->SetEpisode(episode); + + const char *sql=xevent->GetSQL((const char *) EITSOURCE,99,ChannelID); + char *errmsg; + if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK) + { + esyslog("epghandler: %s",errmsg); + sqlite3_free(errmsg); + delete xevent; + xevent=NULL; + } + sqlite3_close(db); + free(epdir); + iconv_close(conv); + return xevent; +} + +void cImport::UpdateXMLTVEvent(const char *EPGFile, sqlite3 *db, const char *Source, tEventID EventID, + tEventID EITEventID, const char *EITDescription) +{ + bool closedb=false; + if (!db) + { + if (sqlite3_open_v2(EPGFile,&db,SQLITE_OPEN_READWRITE,NULL)!=SQLITE_OK) + { + esyslog("epghandler: failed to open %s",EPGFile); + return; + } + closedb=true; + } + + char *sql=NULL; + if (EITDescription) + { + char *eitdescription=strdup(EITDescription); + if (!eitdescription) + { + esyslog("epghandler: out of memory"); + if (closedb) sqlite3_close(db); + return; + } + + string ed=eitdescription; + + int reps; + reps=pcrecpp::RE("'").GlobalReplace("''",&ed); + if (reps) + { + eitdescription=(char *) realloc(eitdescription,ed.size()+1); + strcpy(eitdescription,ed.c_str()); + } + + if (asprintf(&sql,"update epg set eiteventid=%li, eitdescription='%s' where eventid=%li and src='%s'", + (long int) EITEventID,eitdescription,(long int) EventID,Source)==-1) + { + free(eitdescription); + esyslog("epghandler: out of memory"); + if (closedb) sqlite3_close(db); + return; + } + free(eitdescription); + } + else + { + if (asprintf(&sql,"update epg set eiteventid=%li where eventid=%li and src='%s'", + (long int) EITEventID,(long int) EventID,Source)==-1) + { + esyslog("epghandler: out of memory"); + if (closedb) sqlite3_close(db); + return; + } + } + + char *errmsg; + if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK) + { + esyslog("epghandler: %s -> %s",sql,errmsg); + free(sql); + sqlite3_free(errmsg); + if (closedb) sqlite3_close(db); + return; + } + + free(sql); + if (closedb) sqlite3_close(db); + return; +} + +cXMLTVEvent *cImport::SearchXMLTVEvent(const char *EPGFile, const char *ChannelID, const cEvent *event) +{ + + if (!event) return NULL; + if (!EPGFile) return NULL; + + cXMLTVEvent *xevent=NULL; + sqlite3_stmt *stmt=NULL; + sqlite3 *db=NULL; + char *sql=NULL; + + if (sqlite3_open_v2(EPGFile,&db,SQLITE_OPEN_READONLY,NULL)!=SQLITE_OK) + { + esyslog("epghandler: failed to open %s",EPGFile); + return NULL; + } + + if (asprintf(&sql,"select channelid,eventid,starttime,duration,title,origtitle,shorttext,description," \ + "country,year,credits,category,review,rating,starrating,video,audio,season,episode," \ + "mixing,src,eiteventid,eitdescription from epg where eiteventid=%li and channelid='%s' " \ + "order by srcidx asc limit 1",(long int) event->EventID(),ChannelID)==-1) + { + sqlite3_close(db); + esyslog("epghandler: out of memory"); + return NULL; + } + + xevent=PrepareAndReturn(db,sql,stmt); + if (xevent) + { + sqlite3_close(db); + return xevent; + } + + int eventTimeDiff=0; + if (event->Duration()) eventTimeDiff=event->Duration()/4; + if (eventTimeDiff<780) eventTimeDiff=780; + + char *sqltitle=strdup(event->Title()); + + string st=sqltitle; + + int reps; + reps=pcrecpp::RE("'").GlobalReplace("''",&st); + if (reps) + { + sqltitle=(char *) realloc(sqltitle,st.size()+1); + strcpy(sqltitle,st.c_str()); + } + + if (asprintf(&sql,"select channelid,eventid,starttime,duration,title,origtitle,shorttext,description," \ + "country,year,credits,category,review,rating,starrating,video,audio,season,episode," \ + "mixing,src,eiteventid,eitdescription,abs(starttime-%li) as diff from epg where " \ + " (starttime>=%li and starttime<=%li) and title='%s' and channelid='%s' " \ + " order by diff,srcidx asc limit 1",event->StartTime(),event->StartTime()-eventTimeDiff, + event->StartTime()+eventTimeDiff,sqltitle,ChannelID)==-1) + { + free(sqltitle); + sqlite3_close(db); + esyslog("epghandler: out of memory"); + return NULL; + } + free(sqltitle); + + xevent=PrepareAndReturn(db,sql,stmt); + if (xevent) + { + sqlite3_close(db); + return xevent; + } + + sqlite3_close(db); + return NULL; +} + +int cImport::Process(cEPGExecutor &myExecutor) +{ + if (!source) return 0; + time_t begin=time(NULL); + time_t end=begin+(source->DaysInAdvance()*86400); +#if VDRVERSNUM < 10726 && (!EPGHANDLER) + time_t endoneday=begin+86400; +#endif + const cSchedules *schedules=NULL; + cSchedulesLock *schedulesLock; + int l=0; + while (l<300) + { + schedulesLock = new cSchedulesLock(true,200); // wait up to 60 secs for lock! + schedules = cSchedules::Schedules(*schedulesLock); + if (!myExecutor.StillRunning()) + { + delete schedulesLock; + source->Ilog("request to stop from vdr"); + return 0; + } + if (schedules) break; + delete schedulesLock; + l++; + } + + sqlite3 *db=NULL; + if (sqlite3_open_v2(source->EPGFile(),&db,SQLITE_OPEN_READWRITE,NULL)) + { + source->Elog("failed to open %s",source->EPGFile()); + delete schedulesLock; + return 141; + } + + char *sql; + if (asprintf(&sql,"select channelid,eventid,starttime,duration,title,origtitle,shorttext,description," \ + "country,year,credits,category,review,rating,starrating,video,audio,season,episode," \ + "mixing,src,eiteventid,eitdescription from epg where (starttime > %li or " \ + " (starttime + duration) > %li) and (starttime + duration) < %li "\ + " and src='%s'",begin,begin,end,source->Name())==-1) + { + sqlite3_close(db); + source->Elog("out of memory"); + delete schedulesLock; + return 134; + } + + sqlite3_stmt *stmt; + if (sqlite3_prepare_v2(db,sql,strlen(sql),&stmt,NULL)!=SQLITE_OK) + { + sqlite3_close(db); + source->Elog("failed to prepare %s",sql); + free(sql); + delete schedulesLock; + return 141; + } + free(sql); + + int lerr=0; + for (;;) + { + if (sqlite3_step(stmt)==SQLITE_ROW) + { + cXMLTVEvent xevent; + if (FetchXMLTVEvent(stmt,&xevent)) + { + cEPGMapping *map=maps->GetMap(xevent.ChannelID()); + if (!map) + { + if (lerr!=IMPORT_NOMAPPING) + source->Elog("no mapping for channelid %s",xevent.ChannelID()); + lerr=IMPORT_NOMAPPING; + continue; + } + + bool addevents=false; + if ((map->Flags() & OPT_APPEND)==OPT_APPEND) addevents=true; + + for (int i=0; i<map->NumChannelIDs(); i++) + { + cChannel *channel=Channels.GetByChannelID(map->ChannelIDs()[i]); + if (!channel) + { + if (lerr!=IMPORT_NOCHANNEL) + source->Elog("channel %s not found in channels.conf", + *map->ChannelIDs()[i].ToString()); + lerr=IMPORT_NOCHANNEL; + continue; + } + cSchedule* schedule = (cSchedule *) schedules->GetSchedule(channel,addevents); + if (!schedule) + { + if (lerr!=IMPORT_NOSCHEDULE) + source->Elog("cannot get schedule for channel %s%s", + channel->Name(),addevents ? "" : " - try add option"); + lerr=IMPORT_NOSCHEDULE; + continue; + } + + cEvent *event=SearchVDREvent(schedule, &xevent); + + if (addevents && event && (event->EventID() != xevent.EventID())) + { + source->Elog("found another event with different eventid"); + int newflags=map->Flags(); + newflags &=~OPT_APPEND; + map->ChangeFlags(newflags); + } + +#if VDRVERSNUM < 10726 && (!EPGHANDLER) + if ((!addevents) && (xevent.StartTime()>endoneday)) continue; +#endif + PutEvent(source, db, schedule, event, &xevent, map->Flags()); + } + } + } + else + { + break; + } + } + + sqlite3_finalize(stmt); + sqlite3_close(db); + delete schedulesLock; + return 0; +} + +cImport::cImport(cEPGSource *Source, cEPGMappings* Maps, cTEXTMappings *Texts) +{ + maps=Maps; + source=Source; + texts=Texts; + if (source) epgfile=source->EPGFile(); + + char *CodeSet=NULL; + if (setlocale(LC_CTYPE,"")) + CodeSet=nl_langinfo(CODESET); + else + { + char *LangEnv=getenv("LANG"); + if (LangEnv) + { + CodeSet=strchr(LangEnv,'.'); + if (CodeSet) + CodeSet++; + } + } + if (source) source->Dlog("vdr codeset is '%s'",CodeSet ? CodeSet : "US-ASCII//TRANSLIT"); + conv = new cCharSetConv("UTF-8",CodeSet ? CodeSet : "US-ASCII//TRANSLIT"); +} + +cImport::~cImport() +{ + delete conv; +} |