diff options
Diffstat (limited to 'xmltv2vdr.cpp')
-rw-r--r-- | xmltv2vdr.cpp | 1107 |
1 files changed, 281 insertions, 826 deletions
diff --git a/xmltv2vdr.cpp b/xmltv2vdr.cpp index 9984422..c5a2142 100644 --- a/xmltv2vdr.cpp +++ b/xmltv2vdr.cpp @@ -6,841 +6,195 @@ */ #include <vdr/plugin.h> -#include <string.h> -#include <time.h> -#include <sys/wait.h> -#include <sys/ioctl.h> -#include <stdarg.h> -#include <pwd.h> -#include "xmltv2vdr.h" -#include "parse.h" -#include "extpipe.h" -#include "setup.h" - -#if __GNUC__ > 3 -#define UNUSED(v) UNUSED_ ## v __attribute__((unused)) -#else -#define UNUSED(x) x -#endif - -// ------------------------------------------------------------- - -cEPGChannel::cEPGChannel(const char *Name, bool InUse) -{ - name=strdup(Name); - inuse=InUse; -} - -cEPGChannel::~cEPGChannel() -{ - if (name) free((void *) name); -} +#include <vdr/videodir.h> +#include <unistd.h> +#include <getopt.h> -int cEPGChannel::Compare(const cListObject &ListObject) const -{ - cEPGChannel *epgchannel= (cEPGChannel *) &ListObject; - return strcmp(name,epgchannel->Name()); -} +#include "setup.h" +#include "xmltv2vdr.h" // ------------------------------------------------------------- -cEPGExecutor::cEPGExecutor(cEPGSources *Sources) : cThread("xmltv2vdr importer") +cEPGHandler::cEPGHandler(const char *EpgFile, cEPGSources *Sources, cEPGMappings *Maps, + cTEXTMappings *Texts) { - sources=Sources; - textmappings=NULL; epall=false; + epgfile=EpgFile; + maps=Maps; + sources=Sources; + import = new cImport(NULL,Maps,Texts); } -void cEPGExecutor::Action() +cEPGHandler::~cEPGHandler() { - if (!sources) return; - int ret=0; - for (cEPGSource *epgs=sources->First(); epgs; epgs=sources->Next(epgs)) - { - int retries=0; - while (retries<=2) - { - ret=epgs->Execute(*this); - if ((ret>0) && (ret<126) && (retries<2)) - { - epgs->Dlog("waiting 60 seconds"); - int l=0; - while (l<300) - { - struct timespec req; - req.tv_sec=0; - req.tv_nsec=200000000; // 200ms - nanosleep(&req,NULL); - if (!Running()) - { - epgs->Ilog("request to stop from vdr"); - return; - } - l++; - } - retries++; - } - else - { - break; - } - } - if (retries>=2) epgs->Elog("skipping after %i retries",retries); - if (!ret) break; // TODO: check if we must execute second/third source! - } - if (!ret && epall && textmappings) - { - struct passwd pwd,*pwdbuf; - char buf[1024]; - getpwuid_r(getuid(),&pwd,buf,sizeof(buf),&pwdbuf); - if (pwdbuf) - { - char *epdir; - if (asprintf(&epdir,"%s/.eplists/lists",pwdbuf->pw_dir)!=-1) - { - if (!access(epdir,R_OK)) - { - int retries=0; - while (retries<=2) - { - - if (!cParse::AddSeasonEpisode2TimerChannels(epdir,textmappings)) - { - dsyslog("waiting 60 seconds"); - retries++; - } - else - { - break; - } - } - } - free(epdir); - } - } - } - if (!ret) cSchedules::Cleanup(true); + if (import) delete import; } - - -// ------------------------------------------------------------- - -cEPGSource::cEPGSource(const char *Name, const char *ConfDir, cEPGMappings *Maps, cTEXTMappings *Texts) +bool cEPGHandler::IgnoreChannel(const cChannel* Channel) { - dsyslog("xmltv2vdr: '%s' added epgsource",Name); - name=strdup(Name); - confdir=strdup(ConfDir); - pin=NULL; - Log=NULL; - loglen=0; - usepipe=false; - needpin=false; - running=false; - daysinadvance=0; - lastexec=(time_t) 0; - ready2parse=ReadConfig(); - parse=new cParse(this, Maps, Texts); - Dlog("is%sready2parse",(ready2parse && parse) ? " " : " not "); + if (!maps) return false; + if (!Channel) return false; + return maps->IgnoreChannel(Channel); } -cEPGSource::~cEPGSource() +bool cEPGHandler::SetContents(cEvent* UNUSED(Event), uchar* UNUSED(Contents)) { - dsyslog("xmltv2vdr: '%s' epgsource removed",name); - free((void *) name); - free((void *) confdir); - if (pin) free((void *) pin); - if (Log) free((void *) Log); - if (parse) delete parse; + return false; } -bool cEPGSource::ReadConfig() +bool cEPGHandler::SetDescription(cEvent* Event, const char* Description) { - char *fname=NULL; - if (asprintf(&fname,"%s/%s",EPGSOURCES,name)==-1) - { - Elog("out of memory"); - return false; - } - FILE *f=fopen(fname,"r"); - if (!f) - { - Elog("cannot read config file %s",fname); - free(fname); - return false; - } - Dlog("reading source config"); - size_t lsize; - char *line=NULL; - int linenr=1; - while (getline(&line,&lsize,f)!=-1) - { - if (linenr==1) - { - if (!strncmp(line,"pipe",4)) - { - Dlog("is providing data through a pipe"); - usepipe=true; - } - else - { - Dlog("is providing data through a file"); - usepipe=false; - } - char *ndt=strchr(line,';'); - if (ndt) - { - *ndt=0; - ndt++; - char *pn=strchr(ndt,';'); - if (pn) - { - *pn=0; - pn++; - } - /* - newdatatime=atoi(ndt); - if (!newdatatime) Dlog("updates source data @%02i:%02i",1,2); - */ - if (pn) - { - pn=compactspace(pn); - if (pn[0]=='1') - { - Dlog("is needing a pin"); - needpin=true; - } - } - } - } - if (linenr==2) - { - char *semicolon=strchr(line,';'); - if (semicolon) - { - // backward compatibility - *semicolon=0; - semicolon++; - daysmax=atoi(semicolon); - } - else - { - daysmax=atoi(line); - } - Dlog("daysmax=%i",daysmax); - } - if (linenr>2) - { - // channels - char *semicolon=strchr(line,';'); - if (semicolon) *semicolon=0; - char *lf=strchr(line,10); - if (lf) *lf=0; - char *cname=line; - if (line[0]=='*') - { - // backward compatibility - cname++; - } - if (!strchr(cname,' ') && (strlen(cname)>0)) - { - cEPGChannel *epgchannel= new cEPGChannel(cname,false); - if (epgchannel) channels.Add(epgchannel); - } - } - linenr++; - } - if (line) free(line); - channels.Sort(); - fclose(f); - free(fname); + if (!Event) return false; + if (!maps) return false; + if (!import) return false; - /* --------------- */ - - if (asprintf(&fname,"%s/%s",confdir,name)==-1) - { - Elog("out of memory"); - return false; - } - f=fopen(fname,"r+"); - if (!f) + bool special_epall_timer_handling=false; + if (!maps->ProcessChannel(Event->ChannelID())) { - if (errno!=ENOENT) - { - Elog("cannot read config file %s",fname); - free(fname); - return true; - } - /* still no config? -> ok */ - free(fname); - return true; + if (!epall) return false; + if (!Event->HasTimer()) return false; + if (!Event->ShortText()) return false; + special_epall_timer_handling=true; } - Dlog("reading plugin config"); - line=NULL; - linenr=1; - while (getline(&line,&lsize,f)!=-1) - { - if ((linenr==1) && (needpin)) - { - char *lf=strchr(line,10); - if (lf) *lf=0; - if (strcmp(line,"#no pin")) - { - ChangePin(line); - Dlog("pin set"); - } - } - if (linenr==2) - { - daysinadvance=atoi(line); - Dlog("daysinadvance=%i",daysinadvance); - } - if (linenr>2) - { - // channels - char *lf=strchr(line,10); - if (lf) *lf=0; - for (int x=0; x<channels.Count(); x++) - { - if (!strcmp(line,channels.Get(x)->Name())) - { - channels.Get(x)->SetUsage(true); - break; - } - } - } - linenr++; - } - if (line) free(line); - channels.Sort(); - fclose(f); - free(fname); - - return true; -} + int Flags=0; + const char *ChannelID; -int cEPGSource::ReadOutput(char *&result, size_t &l) -{ - int ret=0; - char *fname=NULL; - if (asprintf(&fname,"%s/%s.xmltv",EPGSOURCES,name)==-1) + if (special_epall_timer_handling) { - Elog("out of memory"); - return 134; + cChannel *chan=Channels.GetByChannelID(Event->ChannelID()); + if (!chan) return false; + Flags=USE_SEASON; + ChannelID=chan->Name(); } - Dlog("reading from '%s'",fname); - - int fd=open(fname,O_RDONLY); - if (fd==-1) - { - Elog("failed to open '%s'",fname); - free(fname); - return 157; - } - - struct stat statbuf; - if (fstat(fd,&statbuf)==-1) - { - Elog("failed to stat '%s'",fname); - close(fd); - free(fname); - return 157; - } - l=statbuf.st_size; - result=(char *) malloc(l+1); - if (!result) - { - close(fd); - free(fname); - Elog("out of memory"); - return 134; - } - if (read(fd,result,statbuf.st_size)!=statbuf.st_size) + else { - Elog("failed to read '%s'",fname); - ret=149; - free(result); - result=NULL; + cEPGMapping *map=maps->GetMap(Event->ChannelID()); + if (!map) return false; + Flags=map->Flags(); + ChannelID=map->ChannelName(); } - close(fd); - free(fname); - return ret; -} - -int cEPGSource::Execute(cEPGExecutor &myExecutor) -{ - if (!ready2parse) return false; - if (!parse) return false; - char *r_out=NULL; - char *r_err=NULL; - int l_out=0; - int l_err=0; - int ret=0; - if ((Log) && (lastexec)) + cXMLTVEvent *xevent=import->SearchXMLTVEvent(epgfile,ChannelID,Event); + if (!xevent) { - free(Log); - Log=NULL; - loglen=0; + if (!epall) return false; + xevent=import->AddXMLTVEvent(epgfile,ChannelID,Event,Description); + if (!xevent) return false; } - char *cmd=NULL; - if (asprintf(&cmd,"%s %i '%s'",name,daysinadvance,pin ? pin : "")==-1) - { - Elog("out of memory"); - return 134; - } + bool update=false; + if (!xevent->EITEventID()) update=true; + if (!xevent->EITDescription() && Description) update=true; + if (xevent->EITDescription() && Description && + strcasecmp(xevent->EITDescription(),Description)) update=true; - for (int x=0; x<channels.Count(); x++) - { - if (channels.Get(x)->InUse()) - { - int len=strlen(cmd); - int clen=strlen(channels.Get(x)->Name()); - char *ncmd=(char *) realloc(cmd,len+clen+5); - if (!ncmd) - { - free(cmd); - Elog("out of memory"); - return 134; - } - cmd=ncmd; - strcat(cmd," "); - strcat(cmd,channels.Get(x)->Name()); - strcat(cmd," "); - } - } - char *pcmd=strdup(cmd); - if (pcmd) - { - char *pa=strchr(pcmd,'\''); - char *pe=strchr(pa+1,'\''); - if (pa && pe) - { - pa++; - for (char *c=pa; c<pe; c++) - { - if (c==pa) - { - *c='X'; - } - else - { - *c='@'; - } - } - pe=pcmd; - while (*pe) - { - if (*pe=='@') - { - memmove(pe,pe+1,strlen(pe)); - } - else - { - pe++; - } - } - Ilog("%s",pcmd); - } - free(pcmd); - } - cExtPipe p; - if (!p.Open(cmd)) + if (update) { - free(cmd); - Elog("failed to open pipe"); - return 141; + import->UpdateXMLTVEvent(epgfile,NULL,xevent->Source(), + xevent->EventID(),Event->EventID(),Description); } - free(cmd); - Dlog("executing epgsource"); - running=true; - int fdsopen=2; - while (fdsopen>0) - { - struct pollfd fds[2]; - fds[0].fd=p.Out(); - fds[0].events=POLLIN; - fds[1].fd=p.Err(); - fds[1].events=POLLIN; - if (poll(fds,2,500)>=0) - { - if (fds[0].revents & POLLIN) - { - int n; - if (ioctl(p.Out(),FIONREAD,&n)<0) - { - n=1; - } - r_out=(char *) realloc(r_out, l_out+n+1); - int l=read(p.Out(),r_out+l_out,n); - if (l>0) - { - l_out+=l; - } - } - if (fds[1].revents & POLLIN) - { - int n; - if (ioctl(p.Err(),FIONREAD,&n)<0) - { - n=1; - } - r_err=(char *) realloc(r_err, l_err+n+1); - int l=read(p.Err(),r_err+l_err,n); - if (l>0) - { - l_err+=l; - } - } - if (fds[0].revents & POLLHUP) - { - fdsopen--; - } - if (fds[1].revents & POLLHUP) - { - fdsopen--; - } - if (!myExecutor.StillRunning()) - { - int status; - p.Close(status); - if (r_out) free(r_out); - if (r_err) free(r_err); - Ilog("request to stop from vdr"); - running=false; - return 0; - } - } - else - { - Elog("failed polling"); - break; - } + bool ret=import->PutEvent(sources->GetSource(xevent->Source()),NULL, + (cSchedule *) Event->Schedule(), + Event,xevent,Flags,IMPORT_DESCRIPTION); + delete xevent; + if (!ret) { + dsyslog("xmltv2vdr: failed to put event description!"); } - if (r_out) r_out[l_out]=0; - if (r_err) r_err[l_err]=0; - - if (usepipe) - { - int status; - if (p.Close(status)>0) - { - int returncode=WEXITSTATUS(status); - if ((!returncode) && (r_out)) - { - //Dlog("xmltv2vdr: '%s' parsing output"); - Dlog("parsing output"); - ret=parse->Process(myExecutor,r_out,l_out); - } - else - { - Elog("epgsource returned %i",returncode); - ret=returncode; - } - } - else - { - Elog("failed to execute"); - ret=126; - } - } - else - { - int status; - if (p.Close(status)>0) - { - int returncode=WEXITSTATUS(status); - if (!returncode) - { - size_t l; - char *result=NULL; - ret=ReadOutput(result,l); - if ((!ret) && (result)) - { - ret=parse->Process(myExecutor,result,l); - } - if (result) free(result); - } - else - { - Elog("epgsource returned %i",returncode); - ret=returncode; - } - } - } - if (r_out) free(r_out); - if (!ret) lastexec=time(NULL); - if (r_err) - { - char *saveptr; - char *pch=strtok_r(r_err,"\n",&saveptr); - char *last=(char *) ""; - while (pch) - { - if (strcmp(last,pch)) - { - Elog("%s",pch); - last=pch; - } - pch=strtok_r(NULL,"\n",&saveptr); - } - free(r_err); - } - running=false; return ret; } -void cEPGSource::ChangeChannelSelection(int *Selection) +bool cEPGHandler::SetParentalRating(cEvent* UNUSED(Event), int UNUSED(ParentalRating)) { - for (int i=0; i<channels.Count(); i++) - { - channels.Get(i)->SetUsage(Selection[i]); - } -} - -void cEPGSource::Store(void) -{ - char *fname1=NULL; - char *fname2=NULL; - if (asprintf(&fname1,"%s/%s",confdir,name)==-1) return; - if (asprintf(&fname2,"%s/%s.new",confdir,name)==-1) - { - Elog("out of memory"); - free(fname1); - return; - } - - FILE *w=fopen(fname2,"w+"); - if (!w) - { - Elog("cannot create %s",fname2); - unlink(fname2); - free(fname1); - free(fname2); - return; - } - - if (pin) - { - fprintf(w,"%s\n",pin); - } - else - { - fprintf(w,"#no pin\n"); - } - fprintf(w,"%i\n",DaysInAdvance()); - for (int i=0; i<ChannelList()->Count(); i++) - { - if (ChannelList()->Get(i)->InUse()) - { - fprintf(w,"%s\n",ChannelList()->Get(i)->Name()); - } - } - fclose(w); - - struct stat statbuf; - if (stat(confdir,&statbuf)!=-1) - { - if (chown(fname2,statbuf.st_uid,statbuf.st_gid)) {} - } - - rename(fname2,fname1); - free(fname1); - free(fname2); + return false; } -void cEPGSource::add2Log(const char Prefix, const char *line) +bool cEPGHandler::SetShortText(cEvent* Event, const char* UNUSED(ShortText)) { - if (!line) return; + if (!Event) return false; + if (!maps) return false; + if (!import) return false; - struct tm tm; - time_t now=time(NULL); - localtime_r(&now,&tm); - char dt[30]; - strftime(dt,sizeof(dt)-1,"%H:%M ",&tm); + if (!maps->ProcessChannel(Event->ChannelID())) return false; - loglen+=strlen(line)+3+strlen(dt); - char *nptr=(char *) realloc(Log,loglen); - if (nptr) - { - if (!Log) nptr[0]=0; - Log=nptr; - char prefix[2]; - prefix[0]=Prefix; - prefix[1]=0; - strcat(Log,prefix); - strcat(Log,dt); - strcat(Log,line); - strcat(Log,"\n"); - Log[loglen-1]=0; - } -} + cEPGMapping *map=maps->GetMap(Event->ChannelID()); + if (!map) return false; -void cEPGSource::Elog(const char *format, ...) -{ - va_list ap; - char fmt[255]; - if (snprintf(fmt,sizeof(fmt),"xmltv2vdr '%s' ERROR %s",name,format)==-1) return; - va_start(ap, format); - char *ptr; - if (vasprintf(&ptr,fmt,ap)==-1) return; - va_end(ap); - esyslog(ptr); - add2Log('E',ptr+19+strlen(name)); - free(ptr); -} + cXMLTVEvent *xevent=import->SearchXMLTVEvent(epgfile,map->ChannelName(),Event); + if (!xevent) return false; -void cEPGSource::Dlog(const char *format, ...) -{ - va_list ap; - char fmt[255]; - if (snprintf(fmt,sizeof(fmt),"xmltv2vdr '%s' %s",name,format)==-1) return; - va_start(ap, format); - char *ptr; - if (vasprintf(&ptr,fmt,ap)==-1) return; - va_end(ap); - dsyslog(ptr); - add2Log('D',ptr+13+strlen(name)); - free(ptr); -} + if (!xevent->EITEventID()) import->UpdateXMLTVEvent(epgfile,NULL,xevent->Source(), + xevent->EventID(),Event->EventID()); -void cEPGSource::Ilog(const char *format, ...) -{ - va_list ap; - char fmt[255]; - if (snprintf(fmt,sizeof(fmt),"xmltv2vdr '%s' %s",name,format)==-1) return; - va_start(ap, format); - char *ptr; - if (vasprintf(&ptr,fmt,ap)==-1) return; - va_end(ap); - isyslog(ptr); - add2Log('I',ptr+13+strlen(name)); - free(ptr); + bool ret=import->PutEvent(sources->GetSource(xevent->Source()),NULL, + (cSchedule *) Event->Schedule(),Event,xevent, + map->Flags(),IMPORT_SHORTTEXT); + delete xevent; + if (!ret) { + dsyslog("xmltv2vdr: failed to put event shorttext!"); + } + return ret; } // ------------------------------------------------------------- -bool cPluginXmltv2vdr::epgsourceexists(const char *name) +cEPGTimer::cEPGTimer(const char *EpgFile, cEPGSources *Sources, cEPGMappings *Maps, + cTEXTMappings *Texts) : cThread("xmltv2vdr timer thread") { - if (!epgsources.Count()) return false; - for (cEPGSource *epgs=epgsources.First(); epgs; epgs=epgsources.Next(epgs)) - { - if (!strcmp(epgs->Name(),name)) return true; - } - return false; + epgfile=EpgFile; + sources=Sources; + maps=Maps; + import = new cImport(NULL,Maps,Texts); } -void cPluginXmltv2vdr::removeepgmappings() +void cEPGTimer::Action() { - cEPGMapping *maps; - while ((maps=epgmappings.Last())!=NULL) - { - epgmappings.Del(maps); - } -} + struct stat statbuf; + if (stat(epgfile,&statbuf)==-1) return; // no database? -> exit immediately + if (!statbuf.st_size) return; // no database? -> exit immediately -void cPluginXmltv2vdr::removetextmappings() -{ - cTEXTMapping *maps; - while ((maps=textmappings.Last())!=NULL) + cSchedulesLock *schedulesLock = new cSchedulesLock(true,2000); // wait up to 2 secs for lock! + const cSchedules *schedules = cSchedules::Schedules(*schedulesLock); + if (!schedules) { - textmappings.Del(maps); + delete schedulesLock; + return; } -} -void cPluginXmltv2vdr::removeepgsources() -{ - cEPGSource *epgs; - while ((epgs=epgsources.Last())!=NULL) + for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) { - epgsources.Del(epgs); - } -} + const cEvent *event=Timer->Event(); + if (!event) continue; + if (!event->ShortText()) continue; // no short text -> no episode + if (maps->ProcessChannel(event->ChannelID())) continue; // already processed by xmltv2vdr -cEPGMapping *cPluginXmltv2vdr::EPGMapping(const char *ChannelName) -{ - if (!ChannelName) return NULL; - if (!epgmappings.Count()) return NULL; - for (cEPGMapping *maps=epgmappings.First(); maps; maps=epgmappings.Next(maps)) - { - if (!strcmp(maps->ChannelName(),ChannelName)) return maps; - } - return NULL; -} + cChannel *chan=Channels.GetByChannelID(event->ChannelID()); + if (!chan) continue; + const char *ChannelID=chan->Name(); -cTEXTMapping *cPluginXmltv2vdr::TEXTMapping(const char *Name) -{ - if (!textmappings.Count()) return NULL; - for (cTEXTMapping *textmap=textmappings.First(); textmap; textmap=textmappings.Next(textmap)) - { - if (!strcmp(textmap->Name(),Name)) return textmap; - } - return NULL; -} + cXMLTVEvent *xevent=import->SearchXMLTVEvent(epgfile,ChannelID,event); + if (!xevent) + { + xevent=import->AddXMLTVEvent(epgfile,ChannelID,event,event->Description()); + if (!xevent) continue; + } -void cPluginXmltv2vdr::ReadInEPGSources(bool Reload) -{ - if (Reload) removeepgsources(); - DIR *dir=opendir(EPGSOURCES); - if (!dir) return; - struct dirent *dirent; - while (dirent=readdir(dir)) - { - if (strchr(&dirent->d_name[0],'.')) continue; - if (!epgsourceexists(dirent->d_name)) + int Flags=USE_SEASON; + + cSchedule* schedule = (cSchedule *) schedules->GetSchedule(chan,false); + if (schedule) { - char *path=NULL; - if (asprintf(&path,"%s/%s",EPGSOURCES,dirent->d_name)!=-1) - { - if (access(path,R_OK)!=-1) - { - int fd=open(path,O_RDONLY); - if (fd!=-1) - { - char id[5]; - if (read(fd,id,4)!=4) - { - esyslog("xmltv2vdr: cannot read config file '%s'",dirent->d_name); - } - else - { - id[4]=0; - if (!strcmp(id,"file") || !strcmp(id,"pipe")) - { - epgsources.Add(new cEPGSource(dirent->d_name,confdir,&epgmappings,&textmappings)); - } - else - { - dsyslog("xmltv2vdr: ignoring non config file '%s'",dirent->d_name); - } - close(fd); - } - } - else - { - esyslog("xmltv2vdr: cannot open config file '%s'",dirent->d_name); - } - } - else - { - esyslog("xmltv2vdr: cannot access config file '%s'",dirent->d_name); - } - free(path); - } + import->PutEvent(sources->GetSource(EITSOURCE),NULL,schedule, + (cEvent *) event,xevent,Flags,IMPORT_DESCRIPTION); } + delete xevent; } - closedir(dir); + delete schedulesLock; + cSchedules::Cleanup(true); } -void cPluginXmltv2vdr::SetExecTime(int ExecTime) -{ - exectime=ExecTime; - exectime_t=cTimer::SetTime(time(NULL),cTimer::TimeToInt(exectime)); - if (exectime_t<=time(NULL)) exectime_t+=86000; -} +// ------------------------------------------------------------- cPluginXmltv2vdr::cPluginXmltv2vdr(void) : epgexecutor(&epgsources) { @@ -848,14 +202,17 @@ cPluginXmltv2vdr::cPluginXmltv2vdr(void) : epgexecutor(&epgsources) // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! confdir=NULL; - WakeUp=0; - UpStart=1; - last_exectime_t=0; - exectime=200; + epgfile=NULL; + srcorder=NULL; + epghandler=NULL; + epgtimer=NULL; + last_housetime_t=0; + last_maintime_t=0; + last_epcheck_t=0; + wakeup=0; SetEPAll(false); - SetExecTime(exectime); TEXTMappingAdd(new cTEXTMapping("country",tr("country"))); - TEXTMappingAdd(new cTEXTMapping("date",tr("year"))); + TEXTMappingAdd(new cTEXTMapping("year",tr("year"))); TEXTMappingAdd(new cTEXTMapping("originaltitle",tr("originaltitle"))); TEXTMappingAdd(new cTEXTMapping("category",tr("category"))); TEXTMappingAdd(new cTEXTMapping("actor",tr("actor"))); @@ -868,7 +225,14 @@ cPluginXmltv2vdr::cPluginXmltv2vdr(void) : epgexecutor(&epgsources) TEXTMappingAdd(new cTEXTMapping("presenter",tr("presenter"))); TEXTMappingAdd(new cTEXTMapping("producer",tr("producer"))); TEXTMappingAdd(new cTEXTMapping("writer",tr("writer"))); + TEXTMappingAdd(new cTEXTMapping("video",tr("video"))); + TEXTMappingAdd(new cTEXTMapping("blacknwhite",tr("blacknwhite"))); + TEXTMappingAdd(new cTEXTMapping("audio",tr("audio"))); + TEXTMappingAdd(new cTEXTMapping("dolby",tr("dolby"))); + TEXTMappingAdd(new cTEXTMapping("dolbydigital",tr("dolbydigital"))); + TEXTMappingAdd(new cTEXTMapping("bilingual",tr("bilingual"))); TEXTMappingAdd(new cTEXTMapping("review",tr("review"))); + TEXTMappingAdd(new cTEXTMapping("starrating",tr("starrating"))); TEXTMappingAdd(new cTEXTMapping("season",tr("season"))); TEXTMappingAdd(new cTEXTMapping("episode",tr("episode"))); } @@ -876,17 +240,72 @@ cPluginXmltv2vdr::cPluginXmltv2vdr(void) : epgexecutor(&epgsources) cPluginXmltv2vdr::~cPluginXmltv2vdr() { // Clean up after yourself! +#if VDRVERSNUM < 10726 && (!EPGHANDLER) + delete epghandler; +#endif } +bool cPluginXmltv2vdr::EPGSourceMove(int From, int To) +{ + if (From==To) return false; + sqlite3 *db=NULL; + char *sql=NULL; + if (sqlite3_open_v2(epgfile,&db,SQLITE_OPEN_READWRITE,NULL)==SQLITE_OK) + { + if (asprintf(&sql,"BEGIN TRANSACTION;" \ + "UPDATE epg SET srcidx=98 WHERE srcidx=%i;" \ + "UPDATE epg SET srcidx=%i WHERE srcidx=%i;" \ + "UPDATE epg SET srcidx=%i WHERE srcidx=98;" \ + "COMMIT;", To, From, To, From)==-1) + { + sqlite3_close(db); + return false; + } + if (sqlite3_exec(db,sql,NULL,NULL,NULL)!=SQLITE_OK) + { + free(sql); + sqlite3_close(db); + return false; + } + } + free(sql); + sqlite3_close(db); + epgsources.Move(From,To); + return true; +} + + const char *cPluginXmltv2vdr::CommandLineHelp(void) { // Return a string that describes all known command line options. - return NULL; + return " -E FILE, --epgfile=FILE write the EPG data into the given FILE(default is\n" + " 'epg.db' in the video directory)\n"; } -bool cPluginXmltv2vdr::ProcessArgs(int UNUSED(argc), char *UNUSED(argv[])) +bool cPluginXmltv2vdr::ProcessArgs(int argc, char *argv[]) { - // Implement command line argument processing here if applicable. + // Command line argument processing + static struct option long_options[] = + { + { "epgfile", required_argument, NULL, 'E' + }, + { 0,0,0,0 } + }; + + int c; + while ((c = getopt_long(argc, argv, "E:", long_options, NULL)) != -1) + { + switch (c) + { + case 'E': + if (epgfile) free(epgfile); + epgfile=strdup(optarg); + isyslog("xmltv2vdr: using file '%s' for epgdata",optarg); + break; + default: + return false; + } + } return true; } @@ -900,45 +319,84 @@ bool cPluginXmltv2vdr::Start(void) { // Start any background activities the plugin shall perform. confdir=strdup(ConfigDirectory(PLUGIN_NAME_I18N)); // creates internally the confdir! - cParse::InitLibXML(); - ReadInEPGSources(); - if (UpStart) + if (!epgfile) { - exectime_t=time(NULL)+60; - struct tm tm; - localtime_r(&exectime_t,&tm); - // prevent from getting startet again - exectime=tm.tm_hour*100+tm.tm_min; - } - else - { - if (!exectime_t) - { - exectime_t=time(NULL)-60; - last_exectime_t=exectime_t; - } + if (asprintf(&epgfile,"%s/epg.db",VideoDirectory)==-1)return false; } + cParse::InitLibXML(); + + ReadInEPGSources(); + epghandler = new cEPGHandler(epgfile,&epgsources,&epgmappings,&textmappings); + epgtimer = new cEPGTimer(epgfile,&epgsources,&epgmappings,&textmappings); + + if (sqlite3_threadsafe()==0) esyslog("xmltv2vdr: ERROR sqlite3 not threadsafe!"); return true; } void cPluginXmltv2vdr::Stop(void) { // Stop any background activities the plugin is performing. + cSchedules::Cleanup(true); + epgtimer->Stop(); + delete epgtimer; epgexecutor.Stop(); - removeepgsources(); - removeepgmappings(); - removetextmappings(); + epgsources.Remove(); + epgmappings.Remove(); + textmappings.Remove(); cParse::CleanupLibXML(); if (confdir) { free(confdir); confdir=NULL; } + if (epgfile) + { + free(epgfile); + epgfile=NULL; + } + if (srcorder) + { + free(srcorder); + srcorder=NULL; + } } void cPluginXmltv2vdr::Housekeeping(void) { // Perform any cleanup or other regular tasks. + time_t now=time(NULL); + if (now>(last_housetime_t+3600)) + { + struct stat statbuf; + if (stat(epgfile,&statbuf)!=-1) + { + if (statbuf.st_size) + { + sqlite3 *db=NULL; + if (sqlite3_open_v2(epgfile,&db,SQLITE_OPEN_READWRITE,NULL)==SQLITE_OK) + { + char *sql; + if (asprintf(&sql,"delete from epg where ((starttime+duration) < %li)",now)!=-1) + { + char *errmsg; + if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK) + { + esyslog("xmltv2vdr: %s",errmsg); + sqlite3_free(errmsg); + } + else + { + isyslog("xmltv2vdr: removed %i old entries from db",sqlite3_changes(db)); + sqlite3_exec(db,"VACCUM;",NULL,NULL,NULL); + } + free(sql); + } + } + sqlite3_close(db); + } + } + last_housetime_t=now; + } } void cPluginXmltv2vdr::MainThreadHook(void) @@ -946,12 +404,24 @@ void cPluginXmltv2vdr::MainThreadHook(void) // Perform actions in the context of the main program thread. // WARNING: Use with great care - see PLUGINS.html! time_t now=time(NULL); - if (((now>=exectime_t) && (now<(exectime_t+10))) && - (last_exectime_t!=exectime_t)) + if (now>(last_maintime_t+60)) { - epgexecutor.Start(); - last_exectime_t=exectime_t; - SetExecTime(exectime); + if (!epgexecutor.Active() && (!epgtimer->Active())) + { + if (epgsources.RunItNow()) epgexecutor.Start(); + } + last_maintime_t=now; + } + if (epall) + { + if (now>(last_epcheck_t+600)) + { + if (!epgtimer->Active() && (!epgexecutor.Active())) + { + epgtimer->Start(); + } + last_epcheck_t=now; + } } } @@ -968,19 +438,11 @@ cString cPluginXmltv2vdr::Active(void) time_t cPluginXmltv2vdr::WakeupTime(void) { // Return custom wakeup time for shutdown script - if (WakeUp) - { - time_t Now=time(NULL); - time_t Time=cTimer::SetTime(Now,cTimer::TimeToInt(exectime)); - Time-=180; - if (Time<=Now) - Time=cTimer::IncDay(Time,1); - return Time; - } - else - { - return 0; - } + if (!wakeup) return (time_t) 0; + + time_t nt=epgsources.NextRunTime(); + if (nt) nt-=(time_t) 180; + return nt; } const char *cPluginXmltv2vdr::MainMenuEntry(void) @@ -1007,9 +469,7 @@ bool cPluginXmltv2vdr::SetupParse(const char *Name, const char *Value) if (!strncasecmp(Name,"channel",7)) { if (strlen(Name)<10) return false; - - cEPGMapping *map = new cEPGMapping(&Name[8],Value); - epgmappings.Add(map); + epgmappings.Add(new cEPGMapping(&Name[8],Value)); } else if (!strncasecmp(Name,"textmap",7)) { @@ -1021,25 +481,20 @@ bool cPluginXmltv2vdr::SetupParse(const char *Name, const char *Value) } else { - cTEXTMapping *textmap = new cTEXTMapping(&Name[8],Value); - textmappings.Add(textmap); + textmappings.Add(new cTEXTMapping(&Name[8],Value)); } } - else if (!strcasecmp(Name,"options.exectime")) + else if (!strcasecmp(Name,"options.epall")) { - SetExecTime(atoi(Value)); + SetEPAll((bool) atoi(Value)); } else if (!strcasecmp(Name,"options.wakeup")) { - WakeUp=atoi(Value); + wakeup=(bool) atoi(Value); } - else if (!strcasecmp(Name,"options.upstart")) + else if (!strcasecmp(Name,"source.order")) { - UpStart=atoi(Value); - } - else if (!strcasecmp(Name,"options.epall")) - { - SetEPAll((bool) atoi(Value)); + srcorder=strdup(Value); } else return false; return true; |