diff options
-rw-r--r-- | HISTORY | 7 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | dist/patches/README | 5 | ||||
-rw-r--r-- | dist/patches/vdr-1.6.0-3.epghandler.patch.gz | bin | 0 -> 11544 bytes | |||
-rw-r--r-- | event.cpp | 402 | ||||
-rw-r--r-- | event.h | 221 | ||||
-rw-r--r-- | import.cpp | 1222 | ||||
-rw-r--r-- | import.h | 77 | ||||
-rw-r--r-- | maps.cpp | 116 | ||||
-rw-r--r-- | maps.h | 32 | ||||
-rw-r--r-- | parse.cpp | 1179 | ||||
-rw-r--r-- | parse.h | 178 | ||||
-rw-r--r-- | po/de_DE.po | 155 | ||||
-rw-r--r-- | po/it_IT.po | 152 | ||||
-rw-r--r-- | setup.cpp | 674 | ||||
-rw-r--r-- | setup.h | 23 | ||||
-rw-r--r-- | source.cpp | 930 | ||||
-rw-r--r-- | source.h | 200 | ||||
-rw-r--r-- | xmltv2vdr.cpp | 1107 | ||||
-rw-r--r-- | xmltv2vdr.h | 208 |
20 files changed, 4228 insertions, 2668 deletions
@@ -1,5 +1,12 @@ VDR Plugin 'xmltv2vdr' Revision History --------------------------------------- +2012-04-01: Version 0.1.0 + +- Added EpgHandler support + +2011-11-27: Version 0.0.2 + +- Added eplist support 2010-12-05: Version 0.0.1 @@ -46,8 +46,8 @@ PACKAGE = vdr-$(ARCHIVE) ### Includes and Defines (add further entries here): -PKG-LIBS += libxml-2.0 -PKG-INCLUDES += libxml-2.0 +PKG-LIBS += libxml-2.0 libpcrecpp sqlite3 +PKG-INCLUDES += libxml-2.0 libpcrecpp sqlite3 INCLUDES += -I$(VDRDIR)/include @@ -58,7 +58,7 @@ LIBS += $(shell $(PKG-CONFIG) --libs $(PKG-LIBS)) ### The object files (add further files here): -OBJS = $(PLUGIN).o extpipe.o parse.o setup.o maps.o +OBJS = $(PLUGIN).o extpipe.o parse.o source.o import.o event.o setup.o maps.o ### The main target: @@ -117,6 +117,8 @@ dist: clean @cp -a *.cpp *.h HISTORY COPYING Makefile README po $(TMPDIR)/$(ARCHIVE) @mkdir -p $(TMPDIR)/$(ARCHIVE)/dist/epgdata2xmltv @cp -a dist/epgdata2xmltv/*.cpp dist/epgdata2xmltv/*.h dist/epgdata2xmltv/Makefile dist/epgdata2xmltv/INSTALL dist/epgdata2xmltv/COPYING dist/epgdata2xmltv/epgdata2xmltv.dist dist/epgdata2xmltv/epgdata2xmltv.xsl $(TMPDIR)/$(ARCHIVE)/dist/epgdata2xmltv + @mkdir -p $(TMPDIR)/$(ARCHIVE)/dist/patches + @cp -a dist/patches/* $(TMPDIR)/$(ARCHIVE)/dist/patches @tar czf $(PACKAGE).tgz -C $(TMPDIR) --exclude debian --exclude CVS --exclude .svn $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @echo Distribution package created as $(PACKAGE).tgz diff --git a/dist/patches/README b/dist/patches/README new file mode 100644 index 0000000..b97fd77 --- /dev/null +++ b/dist/patches/README @@ -0,0 +1,5 @@ +vdr-1.6.0-3.epghandler.patch.gz + + Backport of EpgHandler for vdr-1.6.0-3 + copy the file into the vdr source directory and apply with + gzip -cd vdr-1.6.0-3.epghandler.patch.gz | patch -p1 diff --git a/dist/patches/vdr-1.6.0-3.epghandler.patch.gz b/dist/patches/vdr-1.6.0-3.epghandler.patch.gz Binary files differnew file mode 100644 index 0000000..0323d43 --- /dev/null +++ b/dist/patches/vdr-1.6.0-3.epghandler.patch.gz diff --git a/event.cpp b/event.cpp new file mode 100644 index 0000000..19838c8 --- /dev/null +++ b/event.cpp @@ -0,0 +1,402 @@ +/* + * event.cpp: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <vector> +#include <vdr/tools.h> +#include <pcrecpp.h> +#include "event.h" + +extern char *strcatrealloc(char *, const char*); + +cXMLTVStringList::~cXMLTVStringList(void) +{ + free(buf); + Clear(); +} + +void cXMLTVStringList::Clear(void) +{ + for (int i=0; i<Size();i++) + free(At(i)); + cVector< char* >::Clear(); +} + +const char* cXMLTVStringList::toString() +{ + free(buf); + buf=NULL; + + for (int i=0; i<Size();i++) + { + buf=strcatrealloc(buf,operator[](i)); + if (i<Size()-1) buf=strcatrealloc(buf,"@"); + } + + if (!buf) if (asprintf(&buf,"NULL")==-1) buf=NULL; + return buf; +} + +// ------------------------------------------------------------- + +void cXMLTVEvent::SetSource(const char *Source) +{ + source = strcpyrealloc(source, Source); + if (source) source = compactspace(source); +} + +void cXMLTVEvent::SetChannelID(const char *ChannelID) +{ + channelid = strcpyrealloc(channelid, ChannelID); + if (channelid) channelid = compactspace(channelid); +} + +void cXMLTVEvent::SetTitle(const char *Title) +{ + title = strcpyrealloc(title, Title); + if (title) title = compactspace(title); +} + +void cXMLTVEvent::SetOrigTitle(const char *OrigTitle) +{ + origtitle = strcpyrealloc(origtitle, OrigTitle); + if (origtitle) origtitle=compactspace(origtitle); +} + +void cXMLTVEvent::SetShortText(const char *ShortText) +{ + shorttext=strcpyrealloc(shorttext,ShortText); + if (shorttext) shorttext=compactspace(shorttext); +} + +void cXMLTVEvent::SetDescription(const char *Description) +{ + description = strcpyrealloc(description, Description); + if (description) description = compactspace(description); +} + +void cXMLTVEvent::SetEITDescription(const char *EITDescription) +{ + eitdescription = strcpyrealloc(eitdescription, EITDescription); + if (eitdescription) eitdescription = compactspace(eitdescription); +} + +void cXMLTVEvent::SetCountry(const char *Country) +{ + country=strcpyrealloc(country, Country); + if (country) country=compactspace(country); +} + +void cXMLTVEvent::SetAudio(const char *Audio) +{ + audio=strcpyrealloc(audio, Audio); + if (audio) audio = compactspace(audio); +} + +void cXMLTVEvent::SetCredits(const char *Credits) +{ + if (!Credits) return; + char *c=strdup(Credits); + if (!c) return; + char *sp,*tok; + char delim[]="@"; + tok=strtok_r(c,delim,&sp); + while (tok) + { + credits.Append(strdup(tok)); + tok=strtok_r(NULL,delim,&sp); + } + credits.Sort(); + free(c); +} + +void cXMLTVEvent::SetCategory(const char *Category) +{ + if (!Category) return; + char *c=strdup(Category); + if (!c) return; + char *sp,*tok; + char delim[]="@"; + tok=strtok_r(c,delim,&sp); + while (tok) + { + category.Append(strdup(tok)); + tok=strtok_r(NULL,delim,&sp); + } + category.Sort(); + free(c); +} + +void cXMLTVEvent::SetReview(const char *Review) +{ + if (!Review) return; + char *c=strdup(Review); + if (!c) return; + char *sp,*tok; + char delim[]="@"; + tok=strtok_r(c,delim,&sp); + while (tok) + { + review.Append(strdup(tok)); + tok=strtok_r(NULL,delim,&sp); + } + free(c); +} + +void cXMLTVEvent::SetRating(const char *Rating) +{ + if (!Rating) return; + char *c=strdup(Rating); + if (!c) return; + char *sp,*tok; + char delim[]="@"; + tok=strtok_r(c,delim,&sp); + while (tok) + { + rating.Append(strdup(tok)); + tok=strtok_r(NULL,delim,&sp); + } + rating.Sort(); + free(c); +} + +void cXMLTVEvent::SetVideo(const char *Video) +{ + if (!Video) return; + char *c=strdup(Video); + if (!c) return; + char *sp,*tok; + char delim[]="@"; + tok=strtok_r(c,delim,&sp); + while (tok) + { + video.Append(strdup(tok)); + tok=strtok_r(NULL,delim,&sp); + } + free(c); +} + +void cXMLTVEvent::SetStarRating(const char *StarRating) +{ + if (!StarRating) return; + char *c=strdup(StarRating); + if (!c) return; + char *sp,*tok; + char delim[]="@"; + tok=strtok_r(c,delim,&sp); + while (tok) + { + starrating.Append(strdup(tok)); + tok=strtok_r(NULL,delim,&sp); + } + starrating.Sort(); + free(c); +} + +void cXMLTVEvent::AddReview(const char *Review) +{ + review.Append(compactspace(strdup(Review))); +} + +void cXMLTVEvent::AddVideo(const char *VType, const char *VContent) +{ + char *value=NULL; + if (asprintf(&value,"%s|%s",VType,VContent)==-1) return; + video.Append(value); +} + +void cXMLTVEvent::AddRating(const char *System, const char *Rating) +{ + char *value=NULL; + if (asprintf(&value,"%s|%s",System,Rating)==-1) return; + rating.Append(value); + rating.Sort(); +} + +void cXMLTVEvent::AddStarRating(const char *System, const char *Rating) +{ + char *value=NULL; + if (System) + { + if (asprintf(&value,"%s|%s",System,Rating)==-1) return; + } + else + { + if (asprintf(&value,"*|%s",Rating)==-1) return; + } + starrating.Append(value); +} + +void cXMLTVEvent::AddCategory(const char *Category) +{ + category.Append(compactspace(strdup(Category))); + category.Sort(); +} + +void cXMLTVEvent::AddCredits(const char *CreditType, const char *Credit, const char *Addendum) +{ + char *value=NULL; + if (Addendum) + { + if (asprintf(&value,"%s|%s (%s)",CreditType,Credit,Addendum)==-1) return; + } + else + { + if (asprintf(&value,"%s|%s",CreditType,Credit)==-1) return; + } + credits.Append(value); + credits.Sort(); +} + +const char *cXMLTVEvent::GetSQL(const char *Source, int SrcIdx, const char *ChannelID) +{ + if (sql) free(sql); + + const char *cr=credits.toString(); + const char *ca=category.toString(); + const char *re=review.toString(); + const char *ra=rating.toString(); + const char *sr=starrating.toString(); + const char *vi=video.toString(); + + if (asprintf(&sql,"INSERT OR IGNORE INTO epg (src,channelid,eventid,starttime,duration,"\ + "title,origtitle,shorttext,description,country,year,credits,category,"\ + "review,rating,starrating,video,audio,season,episode,mixing,srcidx) "\ + "VALUES (^%s^,^%s^,%i,%li,%i,"\ + "^%s^,^%s^,^%s^,^%s^,^%s^,%i,^%s^,^%s^,"\ + "^%s^,^%s^,^%s^,^%s^,^%s^,%i,%i,%i,%i);"\ + + "UPDATE epg SET duration=%i,starttime=%li,title=^%s^,origtitle=^%s^,"\ + "shorttext=^%s^,description=^%s^,country=^%s^,year=%i,credits=^%s^,category=^%s^,"\ + "review=^%s^,rating=^%s^,starrating=^%s^,video=^%s^,audio=^%s^,season=%i,episode=%i, "\ + "mixing=%i,srcidx=%i " \ + " where changes()=0 and src=^%s^ and channelid=^%s^ and eventid=%i" + , + Source,ChannelID,eventid,starttime,duration,title, + origtitle ? origtitle : "NULL", + shorttext ? shorttext : "NULL", + description ? description : "NULL", + country ? country : "NULL", + year, + cr,ca,re,ra,sr,vi, + audio ? audio : "NULL", + season, episode, mixing, SrcIdx, + + duration,starttime,title, + origtitle ? origtitle : "NULL", + shorttext ? shorttext : "NULL", + description ? description : "NULL", + country ? country : "NULL", + year, + cr,ca,re,ra,sr,vi, + audio ? audio : "NULL", + season, episode, mixing, SrcIdx, + + Source,ChannelID,eventid + + )==-1) return NULL; + + string s=sql; + + int reps; + reps=pcrecpp::RE("'").GlobalReplace("''",&s); + reps+=pcrecpp::RE("\\^").GlobalReplace("'",&s); + reps+=pcrecpp::RE("'NULL'").GlobalReplace("NULL",&s); + if (reps) + { + sql=(char *) realloc(sql,s.size()+1); + strcpy(sql,s.c_str()); + } + return sql; +} + +void cXMLTVEvent::Clear() +{ + if (source) + { + free(source); + source=NULL; + } + if (sql) + { + free(sql); + sql=NULL; + } + if (title) + { + free(title); + title=NULL; + } + if (shorttext) + { + free(shorttext); + shorttext=NULL; + } + if (description) + { + free(description); + description=NULL; + } + if (eitdescription) + { + free(eitdescription); + eitdescription=NULL; + } + if (country) + { + free(country); + country=NULL; + } + if (origtitle) + { + free(origtitle); + origtitle=NULL; + } + if (audio) + { + free(audio); + audio=NULL; + } + if (channelid) + { + free(channelid); + channelid=NULL; + } + year=0; + starttime = 0; + duration = 0; + eventid=eiteventid=0; + video.Clear(); + credits.Clear(); + category.Clear(); + review.Clear(); + rating.Clear(); + starrating.Clear(); + season=0; + episode=0; + mixing=false; +} + +cXMLTVEvent::cXMLTVEvent() +{ + sql=NULL; + source=NULL; + channelid=NULL; + title=NULL; + shorttext=NULL; + description=eitdescription=NULL; + country=NULL; + origtitle=NULL; + audio=NULL; + Clear(); +} + +cXMLTVEvent::~cXMLTVEvent() +{ + Clear(); +} @@ -0,0 +1,221 @@ +/* + * event.h: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef _EVENT_H +#define _EVENT_H + +#include <time.h> +#include <vdr/epg.h> + +class cXMLTVStringList : public cVector<char *> +{ +private: + char *buf; +public: + cXMLTVStringList(int Allocated = 10): cVector<char *>(Allocated) + { + buf=NULL; + } + virtual ~cXMLTVStringList(); + void Sort(void) + { + cVector<char *>::Sort(CompareStrings); + } + const char *toString(); + virtual void Clear(void); +}; + +class cXMLTVEvent +{ +private: + char *title; + char *shorttext; + char *description; + char *eitdescription; + char *country; + char *origtitle; + char *audio; + char *sql; + char *channelid; + char *source; + bool mixing; + int year; + time_t starttime; + int duration; + int season; + int episode; + tEventID eventid; + tEventID eiteventid; + cXMLTVStringList video; + cXMLTVStringList credits; + cXMLTVStringList category; + cXMLTVStringList review; + cXMLTVStringList rating; + cXMLTVStringList starrating; +#if VDRVERSNUM >= 10711 + uchar parentalRating; + uchar contents[MaxEventContents]; +#endif +public: + cXMLTVEvent(); + ~cXMLTVEvent(); + void Clear(); + void SetSource(const char *Source); + void SetChannelID(const char *ChannelID); + void SetTitle(const char *Title); + void SetOrigTitle(const char *OrigTitle); + void SetShortText(const char *ShortText); + void SetDescription(const char *Description); + void SetEITDescription(const char *EITDescription); + void SetCountry(const char *Country); + void SetAudio(const char *Audio); + void AddVideo(const char *VType, const char *VContent); + void AddCredits(const char *CreditType, const char *Credit, const char *Addendum=NULL); + void AddCategory(const char *Category); + void AddReview(const char *Review); + void AddRating(const char *System, const char *Rating); + void AddStarRating(const char *System, const char *Rating); + void SetCredits(const char *Credits); + void SetCategory(const char *Category); + void SetReview(const char *Review); + void SetRating(const char *Rating); + void SetStarRating(const char *StarRating); + void SetVideo(const char *Video); + const char *GetSQL(const char *Source, int SrcIdx, const char *ChannelID); + cXMLTVStringList *Credits() + { + return &credits; + } + cXMLTVStringList *Category() + { + return &category; + } + cXMLTVStringList *Review() + { + return &review; + } + cXMLTVStringList *Rating() + { + return &rating; + } + cXMLTVStringList *StarRating() + { + return &starrating; + } + cXMLTVStringList *Video() + { + return &video; + } + void SetSeason(int Season) + { + season=Season; + } + void SetEpisode(int Episode) + { + episode=Episode; + } + void SetYear(int Year) + { + year=Year; + } + void SetStartTime(time_t StartTime) + { + starttime=StartTime; + } + void SetDuration(int Duration) + { + duration=Duration; + } + void SetEventID(tEventID EventID) + { + eventid=EventID; + } + void SetEITEventID(tEventID EventID) + { + eiteventid=EventID; + } + void SetMixing(void) + { + mixing=true; + } + int Duration() const + { + return duration; + } + time_t StartTime() const + { + return starttime; + } + bool HasTitle(void) + { + return (title!=NULL); + } + const char *ChannelID(void) const + { + return channelid; + } + const char *Source(void) const + { + return source; + } + const char *Title(void) const + { + return title; + } + const char *ShortText(void) const + { + return shorttext; + } + const char *Description(void) const + { + return description; + } + const char *EITDescription(void) const + { + return eitdescription; + } + const char *Country(void) const + { + return country; + } + int Year() const + { + return year; + } + const char *OrigTitle(void) const + { + return origtitle; + } + const char *Audio(void) const + { + return audio; + } + tEventID EventID(void) const + { + return eventid; + } + tEventID EITEventID(void) const + { + return eiteventid; + } + int Season(void) + { + return season; + } + int Episode(void) + { + return episode; + } + bool Mixing(void) + { + return mixing; + } +}; + + + +#endif 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; +} diff --git a/import.h b/import.h new file mode 100644 index 0000000..a2020a6 --- /dev/null +++ b/import.h @@ -0,0 +1,77 @@ +/* + * import.h: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef _IMPORT_H +#define _IMPORT_H + +#include <vdr/epg.h> +#include <vdr/channels.h> +#include <sqlite3.h> + +#include "event.h" +#include "source.h" +#include "maps.h" + +enum +{ + IMPORT_ALL=0, + IMPORT_DESCRIPTION, + IMPORT_SHORTTEXT +}; + +class cEPGSource; +class cEPGExecutor; + +class cImport +{ +private: + struct split + { + char *pointers[256]; + int count; + }; + enum + { + IMPORT_NOERROR=0, + IMPORT_NOSCHEDULE, + IMPORT_NOCHANNEL, + IMPORT_XMLTVERR, + IMPORT_NOMAPPING, + IMPORT_NOCHANNELID, + IMPORT_EMPTYSCHEDULE + }; + cEPGMappings *maps; + cEPGSource *source; + cTEXTMappings *texts; + cCharSetConv *conv; + const char *epgfile; + char *RemoveLastCharFromDescription(char *description); + char *Add2Description(char *description, const char *Value); + char *Add2Description(char *description, const char *Name, const char *Value); + char *Add2Description(char *description, const char *Name, int Value); + char *AddEOT2Description(char *description); + struct split split(char *in, char delim); + cEvent *GetEventBefore(cSchedule* schedule, time_t start); + cEvent *SearchVDREvent(cSchedule* schedule, cXMLTVEvent *event); + bool FetchXMLTVEvent(sqlite3_stmt *stmt, cXMLTVEvent *xevent); + char *RemoveNonASCII(const char *src); + cXMLTVEvent *PrepareAndReturn(sqlite3 *db, char *sql, sqlite3_stmt *stmt); + bool WasChanged(cEvent *event); +public: + cImport(cEPGSource *Source, cEPGMappings *Maps, cTEXTMappings *Texts); + ~cImport(); + int Process(cEPGExecutor &myExecutor); + bool PutEvent(cEPGSource *source, sqlite3 *db, cSchedule* schedule, cEvent *event, + cXMLTVEvent *xevent, int Flags, int Option=IMPORT_ALL); + cXMLTVEvent *SearchXMLTVEvent(const char *EPGFile, const char *ChannelID, const cEvent *event); + void UpdateXMLTVEvent(const char *EPGFile, sqlite3 *db, const char *Source, tEventID EventID, + tEventID EITEventID, const char *EITDescription=NULL); + cXMLTVEvent *AddXMLTVEvent(const char *EPGFile, const char *ChannelID, const cEvent *Event, + const char *EITDescription); +}; + +#endif @@ -28,30 +28,115 @@ void cTEXTMapping::ChangeValue(const char *Value) // -------------------------------------------------------------------------------------------------------- -cEPGMapping::cEPGMapping(const char *ChannelName, const char *Flags_Days_and_Channels) +void cTEXTMappings::Remove() +{ + cTEXTMapping *maps; + while ((maps=Last())!=NULL) + { + Del(maps); + } +} + +cTEXTMapping* cTEXTMappings::GetMap(const char* Name) +{ + if (!Name) return NULL; + if (!Count()) return NULL; + for (int i=0; i<Count();i++) + { + if (!strcmp(Get(i)->Name(),Name)) return Get(i); + } + return NULL; +} + + +// -------------------------------------------------------------------------------------------------------- + +bool cEPGMappings::ProcessChannel(const tChannelID ChannelID) +{ + if (!Count()) return false; + for (int i=0; i<Count();i++) + { + for (int x=0; x<Get(i)->NumChannelIDs(); x++) + { + if (Get(i)->ChannelIDs()[x]==ChannelID) return true; + } + } + return false; +} + +bool cEPGMappings::IgnoreChannel(const cChannel *Channel) +{ + if (!Channel) return false; + if (!Count()) return false; + tChannelID cid=Channel->GetChannelID(); + for (int i=0; i<Count();i++) + { + if ((Get(i)->Flags() & OPT_APPEND)==OPT_APPEND) + { + for (int x=0; x<Get(i)->NumChannelIDs(); x++) + { + if (Get(i)->ChannelIDs()[x]==cid) return true; + } + } + } + return false; +} + +void cEPGMappings::Remove() +{ + cEPGMapping *maps; + while ((maps=Last())!=NULL) + { + Del(maps); + } +} + +cEPGMapping* cEPGMappings::GetMap(const char* ChannelName) +{ + if (!ChannelName) return NULL; + if (!Count()) return NULL; + for (int i=0; i<Count();i++) + { + if (!strcmp(Get(i)->ChannelName(),ChannelName)) return Get(i); + } + return NULL; +} + +cEPGMapping *cEPGMappings::GetMap(tChannelID ChannelID) +{ + if (!Count()) return NULL; + for (int i=0; i<Count();i++) + { + for (int x=0; x<Get(i)->NumChannelIDs(); x++) + { + if (Get(i)->ChannelIDs()[x]==ChannelID) return Get(i); + } + } + return NULL; +} + +// -------------------------------------------------------------------------------------------------------- + +cEPGMapping::cEPGMapping(const char *ChannelName, const char *Flags_and_Channels) { channelname=strdup(ChannelName); dsyslog("xmltv2vdr: added mapping for '%s'",channelname); flags=USE_SHORTTEXT; - days=1; channelids=NULL; numchannelids=0; - if (Flags_Days_and_Channels) + if (Flags_and_Channels) { - char *flags_days_p=(char *) strdup(Flags_Days_and_Channels); - if (!flags_days_p) return; + char *flags_unused_p=(char *) strdup(Flags_and_Channels); + if (!flags_unused_p) return; - char *flags_p=strchr(flags_days_p,';'); + char *flags_p=strchr(flags_unused_p,';'); if (flags_p) { *flags_p=0; flags_p++; - days=atoi(flags_days_p); - if (days<1) days=1; - if (days>365) days=365; char *channels_p=strchr(flags_p,';'); if (channels_p) @@ -62,7 +147,7 @@ cEPGMapping::cEPGMapping(const char *ChannelName, const char *Flags_Days_and_Cha addchannels(channels_p); } } - free(flags_days_p); + free(flags_unused_p); } } @@ -86,7 +171,6 @@ cEPGMapping::cEPGMapping(cEPGMapping©) numchannelids=copy.numchannelids; } flags=copy.flags; - days=copy.days; } int cEPGMapping::compare(const void *a, const void *b) @@ -129,7 +213,7 @@ void cEPGMapping::addchannels(const char *channels) tChannelID ChannelID=tChannelID::FromString(token); if (!(ChannelID==tChannelID::InvalidID)) { - channelids=(tChannelID *) realloc(channelids,(numchannelids+1)*sizeof(tChannelID)); + channelids=(tChannelID *) realloc(channelids,(numchannelids+1)*sizeof(struct tChannelID)); if (!channelids) { free(tmp); @@ -159,7 +243,7 @@ void cEPGMapping::AddChannel(int ChannelNumber) } if (!found) { - channelids=(tChannelID *) realloc(channelids,(numchannelids+1)*sizeof(tChannelID)); + channelids=(tChannelID *) realloc(channelids,(numchannelids+1)*sizeof(struct tChannelID)); if (!channelids) return; channelids[numchannelids]=chan->GetChannelID(); numchannelids++; @@ -214,7 +298,8 @@ void cEPGMapping::RemoveChannel(tChannelID ChannelID, bool MarkOnly) if (found) { channelids[i]=tChannelID::InvalidID; - if (!MarkOnly) { + if (!MarkOnly) + { qsort(channelids,numchannelids,sizeof(tChannelID),compare); numchannelids--; } @@ -240,7 +325,8 @@ void cEPGMapping::RemoveChannel(int ChannelNumber, bool MarkOnly) if (found) { channelids[i]=tChannelID::InvalidID; - if (!MarkOnly) { + if (!MarkOnly) + { qsort(channelids,numchannelids,sizeof(tChannelID),compare); numchannelids--; } @@ -9,10 +9,11 @@ #define _MAPS_H #include <vdr/channels.h> +#include <vdr/tools.h> // Usage field definition -// Bit 0- 5 DAYS in advance +1 -> Range 1-32 +// Bit 0- 5 UNUSED // Bit 6-23 USE_ flags // Bit 24-30 OPT_ flags // Bit 31 always zero @@ -29,8 +30,8 @@ #define USE_REVIEW 0x80 #define USE_VIDEO 0x100 #define USE_AUDIO 0x200 - #define USE_SEASON 0x400 +#define USE_STARRATING 0x800 #define CREDITS_ACTORS 0x100000 #define CREDITS_DIRECTORS 0x200000 @@ -38,7 +39,6 @@ #define CREDITS_LIST 0x800000 #define OPT_MERGELTEXT 0x10000000 -#define OPT_VPS 0x20000000 #define OPT_APPEND 0x40000000 class cTEXTMapping : public cListObject @@ -60,7 +60,12 @@ public: } }; -class cTEXTMappings : public cList<cTEXTMapping> {}; +class cTEXTMappings : public cList<cTEXTMapping> +{ +public: + cTEXTMapping *GetMap(const char *Name); + void Remove(); +}; class cEPGMapping : public cListObject { @@ -70,7 +75,6 @@ private: tChannelID *channelids; int numchannelids; int flags; - int days; void addchannels(const char *channels); public: cEPGMapping(const char *ChannelName, const char *Flags_and_Mappings); @@ -80,10 +84,6 @@ public: { flags=NewFlags; } - void ChangeDays(int NewDays) - { - days=NewDays; - } void ReplaceChannels(int NumChannelIDs, tChannelID *ChannelIDs); void AddChannel(int ChannelNumber); void RemoveChannel(int ChannelNumber, bool MarkOnly=false); @@ -93,10 +93,6 @@ public: { return flags; } - int Days() - { - return days; - } const char *ChannelName() { return channelname; @@ -111,6 +107,14 @@ public: } }; -class cEPGMappings : public cList<cEPGMapping> {}; +class cEPGMappings : public cList<cEPGMapping> +{ +public: + cEPGMapping *GetMap(const char *ChannelName); + cEPGMapping *GetMap(tChannelID ChannelID); + bool ProcessChannel(tChannelID ChannelID); + bool IgnoreChannel(const cChannel *Channel); + void Remove(); +}; #endif @@ -15,195 +15,14 @@ #include <langinfo.h> #include <time.h> #include <pwd.h> +#include <iconv.h> #include <vdr/timers.h> +#include <vdr/tools.h> +#include <sqlite3.h> #include "xmltv2vdr.h" #include "parse.h" -extern char *strcatrealloc(char *dest, const char *src); -#define TEXTMapping(a) TEXTMapping_(a,texts) - -void cXMLTVEvent::SetTitle(const char *Title) -{ - title = strcpyrealloc(title, Title); - if (title) - { - title = compactspace(title); - } -} - -void cXMLTVEvent::SetOrigTitle(const char *OrigTitle) -{ - origtitle = strcpyrealloc(origtitle, OrigTitle); -} - -void cXMLTVEvent::SetShortText(const char *ShortText) -{ - shorttext=strcpyrealloc(shorttext,ShortText); - if (shorttext) shorttext=compactspace(shorttext); -} - -void cXMLTVEvent::SetDescription(const char *Description) -{ - description = strcpyrealloc(description, Description); - if (description) - { - description = compactspace(description); - if (description[strlen(description)-1]!='\n') - description=strcatrealloc(description,"\n"); - } -} - -bool cXMLTVEvent::RemoveLastCharFromDescription() -{ - if (!description) return false; - int len=strlen(description); - if (!len) return true; - description[len-1]=0; - return true; -} - -bool cXMLTVEvent::Add2Description(const char *Value) -{ - description = strcatrealloc(description,Value); - return (description!=NULL); -} - - -bool cXMLTVEvent::Add2Description(const char *Name, const char *Value) -{ - description = strcatrealloc(description,Name); - description = strcatrealloc(description,": "); - description = strcatrealloc(description,Value); - description = strcatrealloc(description,"\n"); - return (description!=NULL); -} - -bool cXMLTVEvent::Add2Description(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!=NULL); -} - -void cXMLTVEvent::SetCountry(const char *Country) -{ - country=strcpyrealloc(country, Country); - if (country) country=compactspace(country); -} - -void cXMLTVEvent::SetReview(const char *Review) -{ - review=strcpyrealloc(review, Review); - if (review) review=compactspace(review); -} - -void cXMLTVEvent::SetRating(const char *System, const char *Rating) -{ - system=strcpyrealloc(system, System); - if (system) system=compactspace(system); - - rating=strcpyrealloc(rating, Rating); - if (rating) rating=compactspace(rating); -} - -void cXMLTVEvent::AddCategory(const char *Category) -{ - categories.Append(compactspace(strdup(Category))); - categories.Sort(); -} - -void cXMLTVEvent::AddCredits(const char *CreditType, const char *Credit, const char *Addendum) -{ - char *value=NULL; - if (Addendum) - { - if (asprintf(&value,"%s|%s (%s)",CreditType,Credit,Addendum)==-1) return; - } - else - { - if (asprintf(&value,"%s|%s",CreditType,Credit)==-1) return; - } - credits.Append(value); - credits.Sort(); -} - -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; - starttime = 0; - duration = 0; - vps= (time_t) 0; - eventid=0; - credits.Clear(); - categories.Clear(); - season=0; - episode=0; -} - -cXMLTVEvent::cXMLTVEvent() -{ - title=NULL; - shorttext=NULL; - description=NULL; - country=NULL; - system=NULL; - review=NULL; - rating=NULL; - origtitle=NULL; - Clear(); -} - -cXMLTVEvent::~cXMLTVEvent() -{ - Clear(); -} - // ------------------------------------------------------- time_t cParse::ConvertXMLTVTime2UnixTime(char *xmltvtime) @@ -277,283 +96,57 @@ time_t cParse::ConvertXMLTVTime2UnixTime(char *xmltvtime) return ret; } -struct cParse::split cParse::split(char *in, char delim) +void cParse::RemoveNonAlphaNumeric(char *String) { - 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; -} + if (!String) return; + int len=strlen(String); + char *p=String; + int pos=0; -char *cParse::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) + while (*p) { - // 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)) + // 0x30 - 0x39 + // 0x41 - 0x5A + // 0x61 - 0x7A + if ((*p<0x30) || (*p>0x7a) || (*p>0x39 && *p<0x41) || (*p>0x5A && *p< 0x61)) { - *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 *cParse::SearchEvent(cSchedule* schedule, cXMLTVEvent *xevent) -{ - if (!xevent) 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) - { - if (!strcmp(f->Title(),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(),start)); - if (diff<=eventTimeDiff) - { - // found event with exact the same title - if (!strcmp(p->Title(),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(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(),xevent->Title()); - f=p; - maxdiff=diff; - } - } - } + memmove(p,p+1,len-pos); + len--; + continue; } + p++; + pos++; } - return f; -} -cEvent *cParse::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)) + // remove leading numbers + len=strlen(String); + p=String; + while (*p) { - if (p->StartTime()>start) + // 0x30 - 0x39 + if ((*p>=0x30) && (*p<=0x39)) { - return (cEvent *) p->Prev(); + memmove(p,p+1,len); + len--; + continue; } - } - if (last) return last; - return NULL; -} - -bool cParse::AddSeasonEpisode2TimerChannels(const char *epdir, cTEXTMappings *texts) -{ - if (!epdir) return false; - if (!texts) return false; - - const cSchedules *schedules=NULL; - cSchedulesLock schedulesLock(true,60000); // wait up to 60 secs for lock! - schedules = cSchedules::Schedules(schedulesLock); - if (!schedules) - { - esyslog("cannot get schedules now, trying later"); - return false; - } - - time_t start=time(NULL); - time_t stop=start+86400; - - for (cTimer *Timer = Timers.First(); Timer; Timer=Timers.Next(Timer)) - { - if (!Timer->Channel()) continue; - - cSchedule* schedule = (cSchedule *) schedules->GetSchedule(Timer->Channel()); - if (!schedule) continue; - - for (cEvent *p = schedule->Events()->First(); p; p = schedule->Events()->Next(p)) + else { - if (p->StartTime()>=start && p->StartTime()<=stop && p->TableID() && p->ShortText()) - { - int season,episode; - if (FetchSeasonEpisode(epdir,p->Title(), p->ShortText(), season, episode)) - { - if ((season) || (episode)) - { - char *description=NULL; - if (p->Description()) description=strdup(p->Description()); - description = strcatrealloc(description,"\n"); - if (season) - { - cTEXTMapping *text=TEXTMapping("season"); - if (text) - { - char *value=NULL; - if (asprintf(&value,"%i",season)!=-1) - { - description = strcatrealloc(description,text->Value()); - description = strcatrealloc(description,": "); - description = strcatrealloc(description,value); - description = strcatrealloc(description,"\n"); - free(value); - } - } - } - if (episode) - { - cTEXTMapping *text=TEXTMapping("episode"); - if (text) - { - char *value=NULL; - if (asprintf(&value,"%i",episode)!=-1) - { - description = strcatrealloc(description,text->Value()); - description = strcatrealloc(description,": "); - description = strcatrealloc(description,value); - description = strcatrealloc(description,"\n"); - free(value); - } - } - } - p->SetDescription(description); - p->SetTableID(0); - free(description); - } - } - } + break; } } - return true; + return; } -bool cParse::FetchSeasonEpisode(const char *epdir, const char *title, const char *shorttext, int &season, int &episode) +bool cParse::FetchSeasonEpisode(iconv_t Conv, const char *EPDir, const char *Title, + const char *ShortText, int &Season, int &Episode) { - if (!epdir) return false; - if (!shorttext) return false; - if (!title) return false; - - /* - char *epfile=NULL; - if (asprintf(&epfile,"%s/%s.episodes",epdir,title)==-1) return false; + if (!EPDir) return false; + if (!ShortText) return false; + if (!Title) return false; + if (Conv==(iconv_t) -1) return false; - struct stat statbuf; - if (stat(epfile,&statbuf)==-1) - { - free(epfile); - return false; - } - */ - DIR *dir=opendir(epdir); + DIR *dir=opendir(EPDir); if (!dir) return false; struct dirent dirent_buf,*dirent; bool found=false; @@ -564,7 +157,7 @@ bool cParse::FetchSeasonEpisode(const char *epdir, const char *title, const char if (dirent->d_name[0]=='.') continue; char *pt=strrchr(dirent->d_name,'.'); if (pt) *pt=0; - if (!strncasecmp(dirent->d_name,title,strlen(dirent->d_name))) + if (!strncasecmp(dirent->d_name,Title,strlen(dirent->d_name))) { found=true; break; @@ -574,7 +167,7 @@ bool cParse::FetchSeasonEpisode(const char *epdir, const char *title, const char if (!found) return false; char *epfile=NULL; - if (asprintf(&epfile,"%s/%s.episodes",epdir,dirent->d_name)==-1) return false; + if (asprintf(&epfile,"%s/%s.episodes",EPDir,dirent->d_name)==-1) return false; FILE *f=fopen(epfile,"r"); if (!f) @@ -583,365 +176,67 @@ bool cParse::FetchSeasonEpisode(const char *epdir, const char *title, const char return false; } - char *line=NULL; - size_t length; - found=false; - while (getline(&line,&length,f)!=-1) + size_t slen=strlen(ShortText); + size_t dlen=4*slen; + char *dshorttext=(char *) calloc(dlen,1); + if (!dshorttext) { - if (line[0]=='#') continue; - char epshorttext[256]=""; - if (sscanf(line,"%d\t%d\t%*d\t%255c",&season,&episode,epshorttext)==3) - { - char *lf=strchr(epshorttext,'\n'); - if (lf) *lf=0; - if (!strncasecmp(shorttext,epshorttext,strlen(epshorttext))) - { - found=true; - break; - } - } + fclose(f); + free(epfile); + return false; } - if (line) free(line); - fclose(f); - - free(epfile); - return found; -} + char *FromPtr=(char *) ShortText; + char *ToPtr=(char *) dshorttext; -bool cParse::PutEvent(cSchedule* schedule, cEvent *event, cXMLTVEvent *xevent, cEPGMapping *map) -{ - if (!schedule) return false; - if (!xevent) return false; - if (!map) return false; - - struct tm tm; - char from[80]; - char till[80]; - time_t start,end; - if (!event) + if (iconv(Conv,&FromPtr,&slen,&ToPtr,&dlen)==(size_t) -1) { - if ((map->Flags() & OPT_APPEND)==OPT_APPEND) - { - 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->SetShortText(xevent->ShortText()); - event->SetDescription(xevent->Description()); - event->SetVersion(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 - { - return true; - } + free(dshorttext); + fclose(f); + free(epfile); + return false; } - if ((map->Flags() & USE_SHORTTEXT)==USE_SHORTTEXT) - { - if (xevent->ShortText() && (strlen(xevent->ShortText())>0)) - { - if (!strcmp(xevent->ShortText(),event->Title())) - { - source->Dlog("title and subtitle equal, clearing subtitle"); - event->SetShortText(""); - } - else - { - event->SetShortText(xevent->ShortText()); - } - } - } + RemoveNonAlphaNumeric(dshorttext); - if (epdir && xevent->ShortText() && (strlen(xevent->ShortText())>0) && ((map->Flags() & USE_SEASON)==USE_SEASON)) - { - // Try to fetch season and episode from eplist - int season,episode; - if (FetchSeasonEpisode(epdir,xevent->Title(),xevent->ShortText(),season,episode)) - { - xevent->SetSeason(season); - xevent->SetEpisode(episode); - } - } - if ((map->Flags() & USE_LONGTEXT)==USE_LONGTEXT) - { - if (xevent->Description() && (strlen(xevent->Description())>0)) - { - event->SetDescription(xevent->Description()); - } - else - { - xevent->SetDescription(event->Description()); - } - } - else - { - xevent->SetDescription(event->Description()); - } - bool addExt=false; - if ((map->Flags() & USE_CREDITS)==USE_CREDITS) + char *line=NULL; + size_t length; + found=false; + while (getline(&line,&length,f)!=-1) { - cStringList *credits=xevent->Credits(); - if (credits->Size()) + if (line[0]=='#') continue; + char epshorttext[256]=""; + char depshorttext[1024]=""; + if (sscanf(line,"%d\t%d\t%*d\t%255c",&Season,&Episode,epshorttext)==3) { - cTEXTMapping *oldtext=NULL; - for (int i=0; i<credits->Size(); i++) - { - char *ctype=strdup((*credits)[i]); - if (ctype) + char *lf=strchr(epshorttext,'\n'); + if (lf) *lf=0; + slen=strlen(epshorttext); + dlen=sizeof(depshorttext); + FromPtr=(char *) epshorttext; + ToPtr=(char *) depshorttext; + if (iconv(Conv,&FromPtr,&slen,&ToPtr,&dlen)!=(size_t) -1) + { + RemoveNonAlphaNumeric(depshorttext); + if (!strncasecmp(dshorttext,depshorttext,strlen(depshorttext))) { - char *cval=strchr(ctype,'|'); - if (cval) - { - *cval=0; - cval++; - bool add=true; - if (((map->Flags() & CREDITS_ACTORS)!=CREDITS_ACTORS) && - (!strcasecmp(ctype,"actor"))) add=false; - if (((map->Flags() & CREDITS_DIRECTORS)!=CREDITS_DIRECTORS) && - (!strcasecmp(ctype,"director"))) add=false; - if (((map->Flags() & CREDITS_OTHERS)!=CREDITS_OTHERS) && - (add) && (strcasecmp(ctype,"actor")) && - (strcasecmp(ctype,"director"))) add=false; - if (add) - { - cTEXTMapping *text=TEXTMapping(ctype); - if ((map->Flags() & CREDITS_LIST)==CREDITS_LIST) - { - if (oldtext!=text) - { - if (oldtext) - { - addExt=xevent->RemoveLastCharFromDescription(); - addExt=xevent->RemoveLastCharFromDescription(); - addExt=xevent->Add2Description("\n"); - } - addExt=xevent->Add2Description(text->Value()); - addExt=xevent->Add2Description(": "); - } - addExt=xevent->Add2Description(cval); - addExt=xevent->Add2Description(", "); - } - else - { - if (text) - { - addExt=xevent->Add2Description(text->Value(),cval); - } - } - oldtext=text; - } - } - free(ctype); + found=true; + break; } } - if ((oldtext) && ((map->Flags() & CREDITS_LIST)==CREDITS_LIST)) - { - addExt=xevent->RemoveLastCharFromDescription(); - addExt=xevent->RemoveLastCharFromDescription(); - addExt=xevent->Add2Description("\n"); - } - } - } - - if ((map->Flags() & USE_COUNTRYDATE)==USE_COUNTRYDATE) - { - if (xevent->Country()) - { - cTEXTMapping *text=TEXTMapping("country"); - if (text) addExt=xevent->Add2Description(text->Value(),xevent->Country()); - } - - if (xevent->Year()) - { - cTEXTMapping *text=TEXTMapping("date"); - if (text) addExt=xevent->Add2Description(text->Value(),xevent->Year()); - } - } - if ((map->Flags() & USE_SEASON)==USE_SEASON) - { - if (xevent->Season()) - { - cTEXTMapping *text=TEXTMapping("season"); - if (text) addExt=xevent->Add2Description(text->Value(),xevent->Season()); - } - - if (xevent->Episode()) - { - cTEXTMapping *text=TEXTMapping("episode"); - if (text) addExt=xevent->Add2Description(text->Value(),xevent->Episode()); - } - } - - if (((map->Flags() & USE_ORIGTITLE)==USE_ORIGTITLE) && (xevent->OrigTitle())) - { - cTEXTMapping *text=TEXTMapping("originaltitle"); - if (text) addExt=xevent->Add2Description(text->Value(),xevent->OrigTitle()); - } - if (((map->Flags() & USE_CATEGORIES)==USE_CATEGORIES) && (xevent->Categories()->Size())) - { - cTEXTMapping *text=TEXTMapping("category"); - if (text) - { - cStringList *categories=xevent->Categories(); - addExt=xevent->Add2Description(text->Value(),(*categories)[0]); - for (int i=1; i<categories->Size(); i++) - { - // prevent duplicates - if (strcasecmp((*categories)[i],(*categories)[i-1])) - addExt=xevent->Add2Description(text->Value(),(*categories)[i]); - } } } - if (((map->Flags() & USE_RATING)==USE_RATING) && (xevent->Rating()) && (xevent->RatingSystem())) - { - addExt=xevent->Add2Description(xevent->RatingSystem(),xevent->Rating()); - } - if (((map->Flags() & USE_REVIEW)==USE_REVIEW) && (xevent->Review())) - { - cTEXTMapping *text=TEXTMapping("review"); - if (text) addExt=xevent->Add2Description(text->Value(),xevent->Review()); - } - if (event->TableID()==0) return true; - if ((map->Flags() & OPT_APPEND)!=OPT_APPEND) - { - 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",event->Title(),from,till); - } - 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) + if (!found) { - sum += *w++; - nleft -= 2; + isyslog("xmltv2vdr: failed to find '%s' for '%s' in eplists",ShortText,Title); } + if (line) free(line); + fclose(f); - if (nleft == 1) - { - u_short answer = 0; - *(u_char*)(&answer) = *(u_char*)w; - sum += answer; - } - return sum; + free(dshorttext); + free(epfile); + return found; } bool cParse::FetchEvent(xmlNodePtr enode) @@ -960,17 +255,17 @@ bool cParse::FetchEvent(xmlNodePtr enode) { if (lang && slang && !xmlStrncasecmp(lang, (const xmlChar *) slang,2)) { - xevent.SetTitle(conv->Convert((const char *) content)); + xevent.SetTitle((const char *) content); } else { - if (!xevent.Title()) + if (!xevent.HasTitle()) { - xevent.SetTitle(conv->Convert((const char *) content)); + xevent.SetTitle((const char *) content); } else { - xevent.SetOrigTitle(conv->Convert((const char *) content)); + xevent.SetOrigTitle((const char *) content); } } xmlFree(content); @@ -983,7 +278,7 @@ bool cParse::FetchEvent(xmlNodePtr enode) xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1); if (content) { - xevent.SetShortText(conv->Convert((const char *) content)); + xevent.SetShortText((const char *) content); xmlFree(content); } } @@ -993,7 +288,7 @@ bool cParse::FetchEvent(xmlNodePtr enode) xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1); if (content) { - xevent.SetDescription(conv->Convert((const char *) content)); + xevent.SetDescription((const char *) content); xmlFree(content); } } @@ -1009,15 +304,9 @@ bool cParse::FetchEvent(xmlNodePtr enode) xmlChar *content=xmlNodeListGetString(vnode->doc,vnode->xmlChildrenNode,1); if (content) { - const char *role=NULL; xmlChar *arole=xmlGetProp(node,(const xmlChar *) "actor role"); - if (arole) - { - role=strdup(conv->Convert((const char *) arole)); - xmlFree(arole); - } - xevent.AddCredits((const char *) vnode->name,conv->Convert((const char *) content),role); - if (role) free((void *) role); + xevent.AddCredits((const char *) vnode->name,(const char *) content,(const char *) arole); + if (arole) xmlFree(arole); xmlFree(content); } } @@ -1026,7 +315,7 @@ bool cParse::FetchEvent(xmlNodePtr enode) xmlChar *content=xmlNodeListGetString(vnode->doc,vnode->xmlChildrenNode,1); if (content) { - xevent.AddCredits((const char *) vnode->name,conv->Convert((const char *) content)); + xevent.AddCredits((const char *) vnode->name,(const char *) content); xmlFree(content); } } @@ -1055,7 +344,7 @@ bool cParse::FetchEvent(xmlNodePtr enode) } else { - xevent.AddCategory(conv->Convert((const char *) content)); + xevent.AddCategory((const char *) content); } xmlFree(content); } @@ -1065,15 +354,69 @@ bool cParse::FetchEvent(xmlNodePtr enode) xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1); if (content) { - xevent.SetCountry(conv->Convert((const char *) content)); + xevent.SetCountry((const char *) content); xmlFree(content); } } else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "video"))) { + xmlNodePtr vnode=node->xmlChildrenNode; + while (vnode) + { + if (vnode->type==XML_ELEMENT_NODE) + { + if ((!xmlStrcasecmp(vnode->name, (const xmlChar *) "colour"))) + { + xmlChar *content=xmlNodeListGetString(vnode->doc,vnode->xmlChildrenNode,1); + if (content) + { + xevent.AddVideo("colour",(const char *) content); + xmlFree(content); + } + } + if ((!xmlStrcasecmp(vnode->name, (const xmlChar *) "aspect"))) + { + xmlChar *content=xmlNodeListGetString(vnode->doc,vnode->xmlChildrenNode,1); + if (content) + { + xevent.AddVideo("aspect",(const char *) content); + xmlFree(content); + } + } + if ((!xmlStrcasecmp(vnode->name, (const xmlChar *) "quality"))) + { + xmlChar *content=xmlNodeListGetString(vnode->doc,vnode->xmlChildrenNode,1); + if (content) + { + xevent.AddVideo("quality",(const char *) content); + xmlFree(content); + } + } + + } + vnode=vnode->next; + } } else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "audio"))) { + xmlNodePtr vnode=node->xmlChildrenNode; + while (vnode) + { + if (vnode->type==XML_ELEMENT_NODE) + { + if ((!xmlStrcasecmp(vnode->name, (const xmlChar *) "stereo"))) + { + xmlChar *content=xmlNodeListGetString(vnode->doc,vnode->xmlChildrenNode,1); + if (content) + { + content=(xmlChar*)strreplace((char *)content," ",""); + xevent.SetAudio((const char *) content); + xmlFree(content); + } + } + } + vnode=vnode->next; + } } else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "rating"))) { @@ -1090,11 +433,7 @@ bool cParse::FetchEvent(xmlNodePtr enode) xmlChar *content=xmlNodeListGetString(vnode->doc,vnode->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); + xevent.AddRating((const char *) system,(const char *) content); xmlFree(content); } } @@ -1104,6 +443,28 @@ bool cParse::FetchEvent(xmlNodePtr enode) xmlFree(system); } } + else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "star-rating"))) + { + xmlChar *system=xmlGetProp(node,(const xmlChar *) "system"); + xmlNodePtr vnode=node->xmlChildrenNode; + while (vnode) + { + if (vnode->type==XML_ELEMENT_NODE) + { + if ((!xmlStrcasecmp(vnode->name, (const xmlChar *) "value"))) + { + xmlChar *content=xmlNodeListGetString(vnode->doc,vnode->xmlChildrenNode,1); + if (content) + { + xevent.AddStarRating((const char *) system,(const char *) content); + xmlFree(content); + } + } + } + vnode=vnode->next; + } + if (system) xmlFree(system); + } else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "review"))) { xmlChar *type=xmlGetProp(node,(const xmlChar *) "type"); @@ -1112,7 +473,7 @@ bool cParse::FetchEvent(xmlNodePtr enode) xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1); if (content) { - xevent.SetReview(conv->Convert((const char *) content)); + xevent.AddReview((const char *) content); xmlFree(content); } xmlFree(type); @@ -1137,27 +498,15 @@ bool cParse::FetchEvent(xmlNodePtr enode) } node=node->next; } - return (xevent.Title()!=NULL); -} -cTEXTMapping *cParse::TEXTMapping_(const char *Name, cTEXTMappings *texts) -{ - if (!texts->Count()) return NULL; - for (cTEXTMapping *textmap=texts->First(); textmap; textmap=texts->Next(textmap)) + int season,episode; + if (FetchSeasonEpisode(conv,epdir,xevent.Title(),xevent.ShortText(),season,episode)) { - if (!strcmp(textmap->Name(),Name)) return textmap; + xevent.SetSeason(season); + xevent.SetEpisode(episode); } - 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; + return xevent.HasTitle(); } int cParse::Process(cEPGExecutor &myExecutor,char *buffer, int bufsize) @@ -1181,30 +530,38 @@ int cParse::Process(cEPGExecutor &myExecutor,char *buffer, int bufsize) return 141; } - const cSchedules *schedules=NULL; - int l=0; - while (l<300) + sqlite3 *db=NULL; + if (sqlite3_open(source->EPGFile(),&db)!=SQLITE_OK) { - cSchedulesLock schedulesLock(true,200); // wait up to 60 secs for lock! - schedules = cSchedules::Schedules(schedulesLock); - if (!myExecutor.StillRunning()) - { - source->Ilog("request to stop from vdr"); - return 0; - } - if (schedules) break; - l++; + source->Elog("failed to open or create %s",source->EPGFile()); + xmlFreeDoc(xmltv); + return 141; } - if (!schedules) + char sql[]="CREATE TABLE IF NOT EXISTS epg (" \ + "src nvarchar(100), channelid nvarchar(255), eventid int, eiteventid int, "\ + "starttime datetime, duration int, title nvarchar(255),origtitle nvarchar(255), "\ + "shorttext nvarchar(255), description text, eitdescription text, " \ + "country nvarchar(255), year int, " \ + "credits text, category text, review text, rating text, " \ + "starrating text, video text, audio text, season int, episode int, mixing int," \ + "srcidx int," \ + "PRIMARY KEY(src, channelid, eventid)" \ + ")"; + + char *errmsg; + if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK) { - source->Elog("cannot get schedules now, trying later"); - return 1; + source->Elog("createdb: %s",errmsg); + sqlite3_free(errmsg); + sqlite3_close(db); + xmlFreeDoc(xmltv); + return 141; } - time_t begin=time(NULL); + time_t begin=time(NULL)-7200; xmlNodePtr node=rootnode->xmlChildrenNode; - cEPGMapping *oldmap=NULL; + int lerr=0; while (node) { @@ -1227,7 +584,7 @@ int cParse::Process(cEPGExecutor &myExecutor,char *buffer, int bufsize) node=node->next; continue; } - cEPGMapping *map=EPGMapping((const char *) channelid); + cEPGMapping *map=maps->GetMap((const char *) channelid); if (!map) { if (lerr!=PARSE_NOMAPPING) @@ -1237,9 +594,7 @@ int cParse::Process(cEPGExecutor &myExecutor,char *buffer, int bufsize) node=node->next; continue; } - int days=map->Days(); - if ((map->Flags() & OPT_APPEND)!=OPT_APPEND) days=1; // only one day with merge - time_t end=begin+(86000*days)+3600; // 1 hour overlap + xmlChar *start,*stop; time_t starttime=(time_t) 0; time_t stoptime=(time_t) 0; @@ -1262,115 +617,53 @@ int cParse::Process(cEPGExecutor &myExecutor,char *buffer, int bufsize) if (!starttime) { if (lerr!=PARSE_XMLTVERR) - source->Elog("xmltv2vdr: '%s' no starttime, check xmltv file"); + source->Elog("no starttime, check xmltv file"); lerr=PARSE_XMLTVERR; xmlFree(channelid); node=node->next; continue; } - if ((starttime<begin) || (starttime>end)) + if (starttime<begin) { xmlFree(channelid); node=node->next; continue; } xevent.Clear(); - - if (oldmap!=map) - { - source->Dlog("processing '%s'",channelid); - source->Dlog("from %s",ctime_r(&begin,(char *) &cbuf)); - source->Dlog("till %s",ctime_r(&end,(char *) &cbuf)); - } - oldmap=map; - xmlFree(channelid); - - if ((map->Flags() & OPT_VPS)==OPT_VPS) - { - 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); + + if ((map->Flags() & OPT_APPEND)!=OPT_APPEND) xevent.SetMixing(); + if (!FetchEvent(node)) // sets xevent { source->Dlog("failed to fetch event"); node=node->next; + xmlFree(channelid); continue; } - 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]); - if (!channel) - { - if (lerr!=PARSE_NOCHANNEL) - source->Elog("channel %s not found in channels.conf", - *map->ChannelIDs()[i].ToString()); - lerr=PARSE_NOCHANNEL; - continue; - } - cSchedule* schedule = (cSchedule *) schedules->GetSchedule(channel,addevents); - if (!schedule) - { - if (lerr!=PARSE_NOSCHEDULE) - source->Elog("cannot get schedule for channel %s%s", - channel->Name(),addevents ? "" : " - try add option"); - lerr=PARSE_NOSCHEDULE; - continue; - } - if (addevents) - { - cEvent *event=SearchEvent(schedule,&xevent); - if (!event) - PutEvent(schedule,event,&xevent,map); - } - else - { - if (!schedule->Events()->Count()) - { - if (lerr!=PARSE_EMPTYSCHEDULE) - source->Elog("cannot merge into empty epg (%s) - try add option", - channel->Name()); - lerr=PARSE_EMPTYSCHEDULE; - } - else - { - cEvent *event=schedule->Events()->Last(); - if (event->StartTime()>xevent.StartTime()) - { - if ((event=SearchEvent(schedule,&xevent))) - { - PutEvent(schedule,event,&xevent,map); - } - else - { - time_t start=xevent.StartTime(); - source->Elog("no event in epg for %s@%s (%s)", - xevent.Title(),ctime_r(&start,(char *) &cbuf), - channel->Name()); - } - } - } - } + char *errmsg; + const char *sql=xevent.GetSQL(source->Name(),source->Index(),(const char *) channelid); + if (sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK) + { + source->Elog("sqlite3: %s",errmsg); + sqlite3_free(errmsg); + xmlFree(channelid); + break; } + xmlFree(channelid); + node=node->next; if (!myExecutor.StillRunning()) { - xmlFreeDoc(xmltv); source->Ilog("request to stop from vdr"); - return 0; + break; } } + sqlite3_close(db); + xmlFreeDoc(xmltv); return 0; } @@ -1385,27 +678,10 @@ void cParse::CleanupLibXML() xmlCleanupParser(); } -cParse::cParse(cEPGSource *Source, cEPGMappings *Maps, cTEXTMappings *Texts) +cParse::cParse(cEPGSource *Source, cEPGMappings *Maps) { source=Source; maps=Maps; - texts=Texts; - - char *CodeSet=NULL; - if (setlocale(LC_CTYPE,"")) - CodeSet=nl_langinfo(CODESET); - else - { - char *LangEnv=getenv("LANG"); - if (LangEnv) - { - CodeSet=strchr(LangEnv,'.'); - if (CodeSet) - CodeSet++; - } - } - source->Dlog("vdr codeset is '%s'",CodeSet ? CodeSet : "US-ASCII//TRANSLIT"); - conv = new cCharSetConv("UTF-8",CodeSet ? CodeSet : "US-ASCII//TRANSLIT"); struct passwd pwd,*pwdbuf; char buf[1024]; @@ -1419,6 +695,10 @@ cParse::cParse(cEPGSource *Source, cEPGMappings *Maps, cTEXTMappings *Texts) free(epdir); epdir=NULL; } + else + { + conv=iconv_open("US-ASCII//TRANSLIT","UTF-8"); + } } else { @@ -1429,6 +709,9 @@ cParse::cParse(cEPGSource *Source, cEPGMappings *Maps, cTEXTMappings *Texts) cParse::~cParse() { - if (epdir) free(epdir); - delete conv; + if (epdir) + { + free(epdir); + iconv_close(conv); + } } @@ -8,201 +8,43 @@ #ifndef _PARSE_H #define _PARSE_H -#include <vdr/tools.h> #include <vdr/epg.h> #include <libxml/parser.h> #include <time.h> #include "maps.h" +#include "event.h" class cEPGExecutor; class cEPGSource; -class cXMLTVEvent -{ -private: - char *title; - char *shorttext; - char *description; - char *country; - char *system; - char *review; - char *rating; - char *origtitle; - int year; - time_t starttime; - int duration; - time_t vps; - int season; - int episode; - tEventID eventid; - cStringList credits; - cStringList categories; -#if VDRVERSNUM >= 10711 - uchar parentalRating; - uchar contents[MaxEventContents]; -#endif -public: - cXMLTVEvent(); - ~cXMLTVEvent(); - bool RemoveLastCharFromDescription(); - bool Add2Description(const char *Name, const char *Value); - bool Add2Description(const char *Name, int Value); - bool Add2Description(const char *Value); - void Clear(); - void SetTitle(const char *Title); - void SetOrigTitle(const char *OrigTitle); - void SetShortText(const char *ShortText); - void SetDescription(const char *Description); - void AddCredits(const char *CreditType, const char *Credit, const char *Addendum=NULL); - void AddCategory(const char *Category); - void SetCountry(const char *Country); - void SetReview(const char *Review); - void SetRating(const char *System, const char *Rating); - void SetSeason(int Season) - { - season=Season; - } - void SetEpisode(int Episode) - { - episode=Episode; - } - void SetYear(int Year) - { - year=Year; - } - void SetStartTime(time_t StartTime) - { - starttime=StartTime; - } - void SetDuration(int Duration) - { - duration=Duration; - } - void SetVps(time_t Vps) - { - vps=Vps; - } - void SetEventID(tEventID EventID) - { - eventid=EventID; - } - time_t Vps(void) const - { - return vps; - } - int Duration() const - { - return duration; - } - time_t StartTime() const - { - return starttime; - } - const char *Title(void) const - { - return title; - } - const char *ShortText(void) const - { - return shorttext; - } - const char *Description(void) const - { - return description; - } - const char *Country(void) const - { - return country; - } - int Year() const - { - return year; - } - const char *Review(void) const - { - return review; - } - const char *Rating(void) const - { - return rating; - } - const char *RatingSystem(void) const - { - return system; - } - const char *OrigTitle(void) const - { - return origtitle; - } - tEventID EventID(void) const - { - return eventid; - } - cStringList *Credits(void) - { - return &credits; - } - cStringList *Categories(void) - { - return &categories; - } - int Season(void) - { - return season; - } - int Episode(void) - { - return episode; - } -}; - class cParse { - struct split - { - char *pointers[256]; - int count; - }; - enum { PARSE_NOERROR=0, - PARSE_NOSCHEDULE, - PARSE_NOCHANNEL, PARSE_XMLTVERR, PARSE_NOMAPPING, - PARSE_NOCHANNELID, - PARSE_EMPTYSCHEDULE + PARSE_NOCHANNELID }; private: + iconv_t conv; + char *epdir; cEPGSource *source; cEPGMappings *maps; - cTEXTMappings *texts; cXMLTVEvent xevent; - cCharSetConv *conv; - char *epdir; - char cbuf[80]; - char *RemoveNonASCII(const char *src); - struct split split(char *in, char delim); - u_long DoSum(u_long sum, const char *buf, int nBytes); - cEvent *SearchEvent(cSchedule* schedule, cXMLTVEvent *xevent); time_t ConvertXMLTVTime2UnixTime(char *xmltvtime); - bool FetchEvent(xmlNodePtr node); - static bool FetchSeasonEpisode(const char *epdir, const char *title, const char *shorttext, - int &season, int &episode); - cEPGMapping *EPGMapping(const char *ChannelName); - static cTEXTMapping *TEXTMapping_(const char *Name, cTEXTMappings *texts); - bool PutEvent(cSchedule* schedule,cEvent *event,cXMLTVEvent *xevent, cEPGMapping *map); - cEvent *GetEventBefore(cSchedule *schedule, time_t start); + bool FetchEvent(xmlNodePtr node); public: - cParse(cEPGSource *Source, cEPGMappings *Maps, cTEXTMappings *Texts); + cParse(cEPGSource *Source, cEPGMappings *Maps); ~cParse(); int Process(cEPGExecutor &myExecutor, char *buffer, int bufsize); + static void RemoveNonAlphaNumeric(char *String); + static bool FetchSeasonEpisode(iconv_t Conv, const char *EPDir, const char *Title, + const char *ShortText, int &Season, int &Episode); static void InitLibXML(); - static void CleanupLibXML(); - static bool AddSeasonEpisode2TimerChannels(const char *epdir, cTEXTMappings *Texts); + static void CleanupLibXML(); }; #endif diff --git a/po/de_DE.po b/po/de_DE.po index bd66905..d6a6fff 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: vdr\n" "Report-Msgid-Bugs-To: <see README>\n" -"POT-Creation-Date: 2012-01-13 14:04+0100\n" +"POT-Creation-Date: 2012-04-01 18:24+0200\n" "PO-Revision-Date: 2010-12-23 23:59+0100\n" "Last-Translator: Jochen Dolze <vdr@dolze.de>\n" "Language-Team: <vdr@linuxtv.org>\n" @@ -16,24 +16,12 @@ msgstr "" msgid "options" msgstr "Optionen" -msgid "update" -msgstr "Ausführung" - -msgid "on time" -msgstr "Zeitpunkt" - -msgid "on start" -msgstr "beim Start" - -msgid "update at" -msgstr "Ausführung um" +msgid "add season/episode on all timers" +msgstr "Staffel/Episode an alle Timer anhängen" msgid "automatic wakeup" msgstr "automatisch Aufwachen" -msgid "add season/episode on all timer channels" -msgstr "Staffel/Episode an alle Timerkanäle anhängen" - msgid "text mapping" msgstr "Textzuordnungen" @@ -43,6 +31,9 @@ msgstr "EPG Quellen" msgid "no epgsources installed" msgstr "Keine EPG Quellen installiert" +msgid "disabled" +msgstr "inaktiv" + msgid "epg source channels" msgstr "EPG Quellkanäle" @@ -64,65 +55,29 @@ msgstr "editieren" msgid "texts" msgstr "Texte" -msgid "country" -msgstr "Land" - -msgid "year" -msgstr "Jahr" +msgid "country and date" +msgstr "Ort und Jahr" -msgid "originaltitle" +msgid "original title" msgstr "Originaltitel" -msgid "director" -msgstr "Regie" - -msgid "actor" -msgstr "Darsteller" - -msgid "writer" -msgstr "Drehbuch" - -msgid "adapter" -msgstr "Adapter" - -msgid "producer" -msgstr "Produzent" - -msgid "composer" -msgstr "Musik" - -msgid "editor" -msgstr "Schnitt" - -msgid "presenter" -msgstr "Moderator" - -msgid "commentator" -msgstr "Bericht" - -msgid "guest" -msgstr "Gaststar" - -msgid "review" -msgstr "Kritik" - msgid "category" msgstr "Kategorie" -msgid "season" -msgstr "Staffel" +msgid "credits" +msgstr "Mitwirkende" -msgid "episode" -msgstr "Episode" +msgid "video informations" +msgstr "Video Informationen" -msgid "country and date" -msgstr "Ort und Jahr" +msgid "audio informations" +msgstr "Audio Informationen" -msgid "original title" -msgstr "Originaltitel" +msgid "review" +msgstr "Kritik" -msgid "credits" -msgstr "Mitwirkende" +msgid "starrating" +msgstr "Wertung" msgid "season and episode" msgstr "Staffel und Episode" @@ -148,6 +103,18 @@ msgstr "Info" msgid "Button$Debug" msgstr "Debug" +msgid "update" +msgstr "Ausführung" + +msgid "on time" +msgstr "Zeitpunkt" + +msgid "on start" +msgstr "beim Start" + +msgid "update at" +msgstr "Ausführung um" + msgid "days in advance" msgstr "Anzahl Tage" @@ -214,9 +181,6 @@ msgstr "Video" msgid "audio" msgstr "Audio" -msgid "vps" -msgstr "VPS" - msgid "epg source channel mappings" msgstr "EPG Quellkanalzuordnungen" @@ -238,6 +202,63 @@ msgstr "Kanaleinstellungen zurücksetzen?" msgid "Button$Choose" msgstr "auswählen" +msgid "country" +msgstr "Land" + +msgid "year" +msgstr "Jahr" + +msgid "originaltitle" +msgstr "Originaltitel" + +msgid "actor" +msgstr "Darsteller" + +msgid "adapter" +msgstr "Adapter" + +msgid "commentator" +msgstr "Bericht" + +msgid "composer" +msgstr "Musik" + +msgid "director" +msgstr "Regie" + +msgid "editor" +msgstr "Schnitt" + +msgid "guest" +msgstr "Gaststar" + +msgid "presenter" +msgstr "Moderator" + +msgid "producer" +msgstr "Produzent" + +msgid "writer" +msgstr "Drehbuch" + +msgid "blacknwhite" +msgstr "s/w" + +msgid "dolby" +msgstr "DolbyDigital 2.0" + +msgid "dolbydigital" +msgstr "DolbyDigital 5.1" + +msgid "bilingual" +msgstr "Zweikanalton" + +msgid "season" +msgstr "Staffel" + +msgid "episode" +msgstr "Episode" + msgid "xmltv2vdr plugin still working" msgstr "xmltv2vdr plugin ist noch aktiv" diff --git a/po/it_IT.po b/po/it_IT.po index 56fdae0..bd360cf 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -4,7 +4,7 @@ msgid "" msgstr "" "Project-Id-Version: vdr\n" "Report-Msgid-Bugs-To: <see README>\n" -"POT-Creation-Date: 2012-01-13 14:04+0100\n" +"POT-Creation-Date: 2012-04-01 18:24+0200\n" "PO-Revision-Date: 2011-03-05 15:45+0100\n" "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" "Language-Team: <vdr@linuxtv.org>\n" @@ -19,24 +19,12 @@ msgstr "" msgid "options" msgstr "Opzioni" -msgid "update" +msgid "add season/episode on all timers" msgstr "" -msgid "on time" -msgstr "" - -msgid "on start" -msgstr "" - -msgid "update at" -msgstr "Ora esecuzione" - msgid "automatic wakeup" msgstr "Risveglio automatico" -msgid "add season/episode on all timer channels" -msgstr "" - msgid "text mapping" msgstr "Mappatura testo" @@ -46,6 +34,9 @@ msgstr "Fonti EPG" msgid "no epgsources installed" msgstr "Nessuna sorgente EPG installata" +msgid "disabled" +msgstr "" + msgid "epg source channels" msgstr "Canali sorgente EPG" @@ -67,65 +58,29 @@ msgstr "Modifica" msgid "texts" msgstr "Testi" -msgid "country" -msgstr "Paese" - -msgid "year" -msgstr "Anno" +msgid "country and date" +msgstr "Paese e data" -msgid "originaltitle" +msgid "original title" msgstr "Titolo originale" -msgid "director" -msgstr "Regista" - -msgid "actor" -msgstr "Attore" - -msgid "writer" -msgstr "Scrittore" - -msgid "adapter" -msgstr "Adattatore" - -msgid "producer" -msgstr "Produttore" - -msgid "composer" -msgstr "Compositore" - -msgid "editor" -msgstr "Editore" - -msgid "presenter" -msgstr "Presentatore" - -msgid "commentator" -msgstr "Commentatore" - -msgid "guest" -msgstr "Ospite" - -msgid "review" -msgstr "Anteprima" - msgid "category" msgstr "Categoria" -msgid "season" -msgstr "" +msgid "credits" +msgstr "Crediti" -msgid "episode" +msgid "video informations" msgstr "" -msgid "country and date" -msgstr "Paese e data" +msgid "audio informations" +msgstr "" -msgid "original title" -msgstr "Titolo originale" +msgid "review" +msgstr "Anteprima" -msgid "credits" -msgstr "Crediti" +msgid "starrating" +msgstr "" msgid "season and episode" msgstr "" @@ -151,6 +106,18 @@ msgstr "" msgid "Button$Debug" msgstr "" +msgid "update" +msgstr "" + +msgid "on time" +msgstr "" + +msgid "on start" +msgstr "" + +msgid "update at" +msgstr "Ora esecuzione" + msgid "days in advance" msgstr "Giorni in anticipo" @@ -217,9 +184,6 @@ msgstr "Video" msgid "audio" msgstr "Audio" -msgid "vps" -msgstr "VPS" - msgid "epg source channel mappings" msgstr "Mappature canali sorgente EPG" @@ -241,8 +205,66 @@ msgstr "" msgid "Button$Choose" msgstr "Scegli" +msgid "country" +msgstr "Paese" + +msgid "year" +msgstr "Anno" + +msgid "originaltitle" +msgstr "Titolo originale" + +msgid "actor" +msgstr "Attore" + +msgid "adapter" +msgstr "Adattatore" + +msgid "commentator" +msgstr "Commentatore" + +msgid "composer" +msgstr "Compositore" + +msgid "director" +msgstr "Regista" + +msgid "editor" +msgstr "Editore" + +msgid "guest" +msgstr "Ospite" + +msgid "presenter" +msgstr "Presentatore" + +msgid "producer" +msgstr "Produttore" + +msgid "writer" +msgstr "Scrittore" + +msgid "blacknwhite" +msgstr "" + +msgid "dolby" +msgstr "" + +msgid "dolbydigital" +msgstr "" + +msgid "bilingual" +msgstr "" + +msgid "season" +msgstr "" + +msgid "episode" +msgstr "" + msgid "xmltv2vdr plugin still working" msgstr "Plugin XMLTV2VDR ancora in esecuzione" msgid "Imports xmltv epg into vdr" msgstr "Importa EPG di XMLTV in VDR" + @@ -8,6 +8,7 @@ #include "setup.h" #include <vdr/osdbase.h> +#include <vdr/i18n.h> #include <time.h> #include <pwd.h> @@ -58,10 +59,8 @@ cMenuSetupXmltv2vdr::cMenuSetupXmltv2vdr(cPluginXmltv2vdr *Plugin) { baseplugin=Plugin; sourcesBegin=sourcesEnd=mappingBegin=mappingEnd=mappingEntry=0; - wakeup=(int) baseplugin->WakeUp; - upstart=(int) baseplugin->UpStart; epall=(int) baseplugin->EPAll(); - exectime=baseplugin->ExecTime(); + wakeup=(int) baseplugin->WakeUp(); cs=NULL; cm=NULL; Output(); @@ -81,14 +80,6 @@ void cMenuSetupXmltv2vdr::Output(void) cOsdItem *first=NewTitle(tr("options")); Add(first,true); - Add(new cMenuEditBoolItem(tr("update"),&upstart,tr("on time"),tr("on start")),true); - updateEntry=Current(); - if (!upstart) - { - Add(new cMenuEditTimeItem(tr("update at"),&exectime),true); - Add(new cMenuEditBoolItem(tr("automatic wakeup"),&wakeup),true); - } - epEntry=0; struct passwd *pw=getpwuid(getuid()); if (pw) @@ -98,11 +89,12 @@ void cMenuSetupXmltv2vdr::Output(void) { if (!access(path,R_OK)) { - Add(new cMenuEditBoolItem(tr("add season/episode on all timer channels"),&epall),true); + Add(new cMenuEditBoolItem(tr("add season/episode on all timers"),&epall),true); } free(path); } } + Add(new cMenuEditBoolItem(tr("automatic wakeup"),&wakeup),true); Add(new cOsdItem(tr("text mapping")),true); mappingEntry=Current(); @@ -124,7 +116,15 @@ void cMenuSetupXmltv2vdr::Output(void) cEPGSource *epgsrc=baseplugin->EPGSource(i); if (epgsrc) { - Add(new cOsdItem(epgsrc->Name()),true); + if (epgsrc->Disabled()) + { + cString buffer = cString::sprintf("%s:\t%s",epgsrc->Name(),tr("disabled")); + Add(new cOsdItem(buffer),true); + } + else + { + Add(new cOsdItem(epgsrc->Name()),true); + } } } sourcesEnd=Current(); @@ -192,15 +192,32 @@ void cMenuSetupXmltv2vdr::generatesumchannellist() void cMenuSetupXmltv2vdr::Store(void) { - SetupStore("options.exectime",exectime); - SetupStore("options.wakeup",wakeup); - SetupStore("options.upstart",upstart); - SetupStore("options.epall",epall); - baseplugin->UpStart=upstart; - baseplugin->WakeUp=wakeup; - baseplugin->SetExecTime(exectime); + char *order=NULL; + for (int i=0; i<baseplugin->EPGSourceCount(); i++) + { + cEPGSource *epgsrc=baseplugin->EPGSource(i); + if (epgsrc && epgsrc->Name()) + { + if (epgsrc->Disabled()) + { + order=strcatrealloc(order,"-"); + } + order=strcatrealloc(order,epgsrc->Name()); + if (i<baseplugin->EPGSourceCount()-1) order=strcatrealloc(order,","); + } + } + + if (order) + { + SetupStore("source.order",order); + free(order); + } + + SetupStore("options.epall",epall); + SetupStore("options.wakeup",wakeup); baseplugin->SetEPAll((bool) epall); + baseplugin->SetWakeUp((bool) wakeup); } eOSState cMenuSetupXmltv2vdr::edit() @@ -232,10 +249,6 @@ eOSState cMenuSetupXmltv2vdr::ProcessKey(eKeys Key) case osContinue: if ((Key==kLeft) || (Key==kRight) || (Key==kLeft|k_Repeat) || (Key==kRight|k_Repeat)) { - if (Current()==updateEntry) Output(); - } - if ((Key==kLeft) || (Key==kRight) || (Key==kLeft|k_Repeat) || (Key==kRight|k_Repeat)) - { if ((epEntry) && (Current()==epEntry)) Output(); } if ((Key==kDown) || (Key==kUp) || (Key==kDown|k_Repeat) || (Key==kUp|k_Repeat)) @@ -267,6 +280,58 @@ eOSState cMenuSetupXmltv2vdr::ProcessKey(eKeys Key) break; case osUnknown: + if (Key==k0) + { + // disable/enable + if ((Current()>=sourcesBegin) && (Current()<=sourcesEnd)) + { + int srcid=Current()-sourcesBegin; + cEPGSource *src=baseplugin->EPGSource(srcid); + if (src) + { + if (src->Disabled()) + { + src->Enable(); + } + else + { + src->Disable(); + } + Output(); + Store(); + } + } + } + if (Key==kRed) + { + // move up + if ((Current()>sourcesBegin) && (Current()<=sourcesEnd)) + { + int From=Current()-sourcesBegin; + int To=From-1; + if (baseplugin->EPGSourceMove(From,To)) + { + CursorUp(); + Output(); + Store(); + } + } + } + if (Key==kGreen) + { + // move down + if ((Current()>=sourcesBegin) && (Current()<sourcesEnd)) + { + int From=Current()-sourcesBegin; + int To=From+1; + if (baseplugin->EPGSourceMove(From,To)) + { + CursorDown(); + Output(); + Store(); + } + } + } if ((Key==kOk) || (Key==kBlue)) { state=edit(); @@ -305,187 +370,38 @@ cMenuSetupXmltv2vdrTextMap::cMenuSetupXmltv2vdrTextMap(cPluginXmltv2vdr *Plugin) cTEXTMapping *textmap; - textmap=baseplugin->TEXTMapping("country"); - if (textmap) - { - strn0cpy(country,textmap->Value(),sizeof(country)-1); - } - else - { - strcpy(country,tr("country")); - } - - textmap=baseplugin->TEXTMapping("date"); - if (textmap) - { - strn0cpy(date,textmap->Value(),sizeof(date)-1); - } - else - { - strcpy(date,tr("year")); - } - - textmap=baseplugin->TEXTMapping("originaltitle"); - if (textmap) - { - strn0cpy(originaltitle,textmap->Value(),sizeof(originaltitle)-1); - } - else - { - strcpy(originaltitle,tr("originaltitle")); - } - - textmap=baseplugin->TEXTMapping("director"); - if (textmap) - { - strn0cpy(director,textmap->Value(),sizeof(director)-1); - } - else - { - strcpy(director,tr("director")); - } - - textmap=baseplugin->TEXTMapping("actor"); - if (textmap) - { - strn0cpy(actor,textmap->Value(),sizeof(actor)-1); - } - else - { - strcpy(actor,tr("actor")); - } - - textmap=baseplugin->TEXTMapping("writer"); - if (textmap) - { - strn0cpy(writer,textmap->Value(),sizeof(writer)-1); - } - else - { - strcpy(writer,tr("writer")); - } - - textmap=baseplugin->TEXTMapping("adapter"); - if (textmap) - { - strn0cpy(adapter,textmap->Value(),sizeof(adapter)-1); - } - else - { - strcpy(adapter,tr("adapter")); - } - - textmap=baseplugin->TEXTMapping("producer"); - if (textmap) - { - strn0cpy(producer,textmap->Value(),sizeof(producer)-1); - } - else - { - strcpy(producer,tr("producer")); - } - - textmap=baseplugin->TEXTMapping("composer"); - if (textmap) - { - strn0cpy(composer,textmap->Value(),sizeof(composer)-1); - } - else - { - strcpy(composer,tr("composer")); - } - - textmap=baseplugin->TEXTMapping("editor"); - if (textmap) - { - strn0cpy(editor,textmap->Value(),sizeof(editor)-1); - } - else - { - strcpy(editor,tr("editor")); - } - - textmap=baseplugin->TEXTMapping("presenter"); - if (textmap) - { - strn0cpy(presenter,textmap->Value(),sizeof(presenter)-1); - } - else - { - strcpy(presenter,tr("presenter")); - } - - textmap=baseplugin->TEXTMapping("commentator"); - if (textmap) - { - strn0cpy(commentator,textmap->Value(),sizeof(commentator)-1); - } - else - { - strcpy(commentator,tr("commentator")); - } - - textmap=baseplugin->TEXTMapping("guest"); - if (textmap) - { - strn0cpy(guest,textmap->Value(),sizeof(guest)-1); - } - else - { - strcpy(guest,tr("guest")); - } - - textmap=baseplugin->TEXTMapping("review"); - if (textmap) - { - strn0cpy(review,textmap->Value(),sizeof(review)-1); - } - else - { - strcpy(review,tr("review")); - } - - textmap=baseplugin->TEXTMapping("category"); - if (textmap) - { - strn0cpy(category,textmap->Value(),sizeof(category)-1); - } - else - { - strcpy(category,tr("category")); - } - - textmap=baseplugin->TEXTMapping("season"); - if (textmap) - { - strn0cpy(season,textmap->Value(),sizeof(season)-1); - } - else - { - strcpy(season,tr("season")); - } - - textmap=baseplugin->TEXTMapping("episode"); - if (textmap) - { - strn0cpy(episode,textmap->Value(),sizeof(episode)-1); - } - else - { - strcpy(episode,tr("episode")); - } +#define settval(dummy) textmap=baseplugin->TEXTMapping(#dummy); \ + if (textmap) { \ + strn0cpy(dummy,textmap->Value(),sizeof(dummy)-1); \ + } else { \ + strcpy(dummy,tr(#dummy)); \ + } Add(NewTitle(tr("country and date"))); + settval(country); + settval(year); Add(new cMenuEditStrItem("country",country,sizeof(country))); - Add(new cMenuEditStrItem("date",date,sizeof(date))); + Add(new cMenuEditStrItem("year",year,sizeof(year))); Add(NewTitle(tr("original title"))); + settval(originaltitle); Add(new cMenuEditStrItem("originaltitle",originaltitle,sizeof(originaltitle))); Add(NewTitle(tr("category"))); + settval(category); Add(new cMenuEditStrItem("category",category,sizeof(category))); Add(NewTitle(tr("credits"))); + settval(director); + settval(actor); + settval(writer); + settval(adapter); + settval(producer); + settval(composer); + settval(editor); + settval(presenter); + settval(commentator); + settval(guest); Add(new cMenuEditStrItem("actor",actor,sizeof(actor))); Add(new cMenuEditStrItem("guest",guest,sizeof(guest))); Add(new cMenuEditStrItem("director",director,sizeof(director))); @@ -497,10 +413,33 @@ cMenuSetupXmltv2vdrTextMap::cMenuSetupXmltv2vdrTextMap(cPluginXmltv2vdr *Plugin) Add(new cMenuEditStrItem("commentator",commentator,sizeof(commentator))); Add(new cMenuEditStrItem("presenter",presenter,sizeof(presenter))); + Add(NewTitle(tr("video informations"))); + settval(video); + settval(blacknwhite); + Add(new cMenuEditStrItem("video",video,sizeof(video))); + Add(new cMenuEditStrItem("blackandwhite",blacknwhite,sizeof(blacknwhite))); + + Add(NewTitle(tr("audio informations"))); + settval(audio); + settval(dolby); + settval(dolbydigital); + settval(bilingual); + Add(new cMenuEditStrItem("audio",audio,sizeof(audio))); + Add(new cMenuEditStrItem("dolby",dolby,sizeof(dolby))); + Add(new cMenuEditStrItem("dolby digital",dolbydigital,sizeof(dolbydigital))); + Add(new cMenuEditStrItem("bilingual",bilingual,sizeof(bilingual))); + Add(NewTitle(tr("review"))); + settval(review); Add(new cMenuEditStrItem("review",review,sizeof(review))); + Add(NewTitle(tr("starrating"))); + settval(starrating); + Add(new cMenuEditStrItem("starrating",starrating,sizeof(starrating))); + Add(NewTitle(tr("season and episode"))); + settval(season); + settval(episode); Add(new cMenuEditStrItem("season",season,sizeof(season))); Add(new cMenuEditStrItem("episode",episode,sizeof(episode))); @@ -510,162 +449,41 @@ void cMenuSetupXmltv2vdrTextMap::Store() { if (!baseplugin) return; cTEXTMapping *textmap; - textmap=baseplugin->TEXTMapping("country"); - if (textmap) - { - textmap->ChangeValue(country); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("country",country)); - } - textmap=baseplugin->TEXTMapping("date"); - if (textmap) - { - textmap->ChangeValue(date); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("date",date)); - } - textmap=baseplugin->TEXTMapping("originaltitle"); - if (textmap) - { - textmap->ChangeValue(originaltitle); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("originaltitle",originaltitle)); - } - textmap=baseplugin->TEXTMapping("category"); - if (textmap) - { - textmap->ChangeValue(category); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("category",category)); - } - textmap=baseplugin->TEXTMapping("actor"); - if (textmap) - { - textmap->ChangeValue(actor); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("actor",actor)); - } - textmap=baseplugin->TEXTMapping("adapter"); - if (textmap) - { - textmap->ChangeValue(adapter); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("adapter",adapter)); - } - textmap=baseplugin->TEXTMapping("commentator"); - if (textmap) - { - textmap->ChangeValue(commentator); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("commentator",commentator)); - } - textmap=baseplugin->TEXTMapping("composer"); - if (textmap) - { - textmap->ChangeValue(composer); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("composer",composer)); - } - textmap=baseplugin->TEXTMapping("director"); - if (textmap) - { - textmap->ChangeValue(director); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("director",director)); - } - textmap=baseplugin->TEXTMapping("editor"); - if (textmap) - { - textmap->ChangeValue(editor); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("editor",editor)); - } - textmap=baseplugin->TEXTMapping("guest"); - if (textmap) - { - textmap->ChangeValue(guest); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("guest",guest)); - } - textmap=baseplugin->TEXTMapping("presenter"); - if (textmap) - { - textmap->ChangeValue(presenter); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("presenter",presenter)); - } - textmap=baseplugin->TEXTMapping("producer"); - if (textmap) - { - textmap->ChangeValue(producer); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("producer",producer)); - } - textmap=baseplugin->TEXTMapping("writer"); - if (textmap) - { - textmap->ChangeValue(writer); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("writer",writer)); - } - textmap=baseplugin->TEXTMapping("review"); - if (textmap) - { - textmap->ChangeValue(review); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("review",review)); - } - textmap=baseplugin->TEXTMapping("season"); - if (textmap) - { - textmap->ChangeValue(season); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("season",season)); - } - textmap=baseplugin->TEXTMapping("episode"); - if (textmap) - { - textmap->ChangeValue(episode); - } - else - { - baseplugin->TEXTMappingAdd(new cTEXTMapping("episode",episode)); - } + +#define savetval(dummy) textmap=baseplugin->TEXTMapping(#dummy); \ + if (textmap) { \ + textmap->ChangeValue(dummy); \ + } else { \ + baseplugin->TEXTMappingAdd(new cTEXTMapping(#dummy,dummy)); \ + } + + savetval(country); + savetval(year); + savetval(originaltitle); + savetval(category); + savetval(director); + savetval(actor); + savetval(writer); + savetval(adapter); + savetval(producer); + savetval(composer); + savetval(editor); + savetval(presenter); + savetval(commentator); + savetval(guest); + savetval(video); + savetval(blacknwhite); + savetval(audio); + savetval(dolby); + savetval(dolbydigital); + savetval(bilingual); + savetval(review); + savetval(starrating); + savetval(season); + savetval(episode); SetupStore("textmap.country",country); - SetupStore("textmap.date",date); + SetupStore("textmap.year",year); SetupStore("textmap.originaltitle",originaltitle); SetupStore("textmap.category",category); SetupStore("textmap.actor",actor); @@ -678,7 +496,14 @@ void cMenuSetupXmltv2vdrTextMap::Store() SetupStore("textmap.presenter",presenter); SetupStore("textmap.producer",producer); SetupStore("textmap.writer",writer); + SetupStore("textmap.video",video); + SetupStore("textmap.blacknwhite",blacknwhite); + SetupStore("textmap.audio",audio); + SetupStore("textmap.dolby",dolby); + SetupStore("textmap.dolbydigital",dolbydigital); + SetupStore("textmap.bilingual",bilingual); SetupStore("textmap.review",review); + SetupStore("textmap.starrating",starrating); SetupStore("textmap.season",season); SetupStore("textmap.episode",episode); } @@ -877,21 +702,53 @@ cMenuSetupXmltv2vdrChannelSource::cMenuSetupXmltv2vdrChannelSource(cPluginXmltv2 menu=Menu; baseplugin=Plugin; sel=NULL; + + day=0; + weekday=127; + start=0; + upstart=1; + days=0; pin[0]=0; + updateEntry=0; + epgsrc=baseplugin->EPGSource(Index); if (!epgsrc) return; SetSection(cString::sprintf("%s '%s' : %s",trVDR("Plugin"), baseplugin->Name(), epgsrc->Name())); - time_t day; - int weekday,start; - + upstart=epgsrc->ExecUpStart(); + weekday=epgsrc->ExecWeekDay(); + start=epgsrc->ExecTime(); + days=epgsrc->DaysInAdvance(); + + output(); +} + +cMenuSetupXmltv2vdrChannelSource::~cMenuSetupXmltv2vdrChannelSource() +{ + if (menu) + { + menu->Output(); + menu->ClearCS(); + } + if (sel) delete [] sel; +} + +void cMenuSetupXmltv2vdrChannelSource::output(void) +{ + Clear(); + Add(NewTitle(tr("options"))); - Add(new cMenuEditDateItem(trVDR("Day"),&day,&weekday)); - Add(new cMenuEditTimeItem(trVDR("Start"),&start)); - days=epgsrc->DaysInAdvance(); + + Add(new cMenuEditBoolItem(tr("update"),&upstart,tr("on time"),tr("on start")),true); + updateEntry=Current(); + if (!upstart) + { + Add(new cMenuEditDateItem(trVDR("Day"),&day,&weekday)); + Add(new cMenuEditTimeItem(tr("update at"),&start)); + } Add(new cMenuEditIntItem(tr("days in advance"),&days,1,epgsrc->DaysMax())); if (epgsrc->NeedPin()) { @@ -922,64 +779,44 @@ cMenuSetupXmltv2vdrChannelSource::cMenuSetupXmltv2vdrChannelSource(cPluginXmltv2 } Add(new cMenuEditBoolItem(channellist->Get(i)->Name(),&sel[i],"",tr("selected"))); } + Display(); } -cMenuSetupXmltv2vdrChannelSource::~cMenuSetupXmltv2vdrChannelSource() +eOSState cMenuSetupXmltv2vdrChannelSource::ProcessKey(eKeys Key) { - if (menu) + eOSState state = cOsdMenu::ProcessKey(Key); + if (HasSubMenu()) return osContinue; + switch (state) { - menu->Output(); - menu->ClearCS(); + case osContinue: + if ((Key==kLeft) || (Key==kRight) || (Key==kLeft|k_Repeat) || (Key==kRight|k_Repeat)) + { + if (Current()==updateEntry) output(); + } + + case osUnknown: + if (Key==kOk) + { + Store(); + state=osBack; + } + + default: + break; } - if (sel) delete [] sel; + return state; } void cMenuSetupXmltv2vdrChannelSource::Store(void) { if ((!baseplugin) || (!sel) || (!epgsrc)) return; + epgsrc->ChangeExec(upstart,start,weekday); epgsrc->ChangeChannelSelection(sel); epgsrc->ChangeDaysInAdvance(days); if (epgsrc->NeedPin()) epgsrc->ChangePin(pin); epgsrc->Store(); - - cEPGChannels *channellist=epgsrc->ChannelList(); - if (channellist) - { - for (int i=0; i<channellist->Count(); i++) - { - if (channellist->Get(i)->InUse()) - { - cEPGMapping *epgmap=baseplugin->EPGMapping(channellist->Get(i)->Name()); - if (epgmap) - { - if (epgmap->Days()>days) - { - char *name=NULL; - if (asprintf(&name,"channel.%s",channellist->Get(i)->Name())==-1) return; - - char *value=NULL; - if (asprintf(&value,"%i;%i;",days,epgmap->Flags())==-1) - { - free(name); - return; - } - for (int i=0; i<epgmap->NumChannelIDs(); i++) - { - cString ChannelID = epgmap->ChannelIDs()[i].ToString(); - value=strcatrealloc(value,*ChannelID); - if (i<epgmap->NumChannelIDs()-1) value=strcatrealloc(value,";"); - } - epgmap->ChangeDays(days); - SetupStore(name,value); - free(name); - free(value); - } - } - } - } - } } // -------------------------------------------------------------------------------------------------------- @@ -990,7 +827,6 @@ cMenuSetupXmltv2vdrChannelMap::cMenuSetupXmltv2vdrChannelMap(cPluginXmltv2vdr *P menu=Menu; hasmaps=false; flags=0; - days=1; if (Index>menu->ChannelList()->Size()) return; channel=(*menu->ChannelList())[Index]; if (!channel) return; @@ -1012,8 +848,6 @@ cMenuSetupXmltv2vdrChannelMap::cMenuSetupXmltv2vdrChannelMap(cPluginXmltv2vdr *P SetTitle(title); flags=map->Flags(); - days=map->Days(); - daysmax=getdaysmax(); c1=c2=c3=cm=0; SetHelp(NULL,NULL,tr("Button$Reset"),tr("Button$Copy")); output(); @@ -1060,12 +894,6 @@ int cMenuSetupXmltv2vdrChannelMap::getdaysmax() return ret; } -cOsdItem *cMenuSetupXmltv2vdrChannelMap::optionN(const char *s, int num) -{ - cString buffer = cString::sprintf("%s:\t%i", s, num); - return new cOsdItem(buffer,osUnknown,false); -} - cOsdItem *cMenuSetupXmltv2vdrChannelMap::option(const char *s, bool yesno) { cString buffer = cString::sprintf("%s:\t%s", s, yesno ? trVDR("yes") : trVDR("no")); @@ -1086,7 +914,6 @@ void cMenuSetupXmltv2vdrChannelMap::output(void) c1=Current(); if ((flags & OPT_APPEND)!=OPT_APPEND) { - Add(optionN(tr("days in advance"),1),true); Add(new cMyMenuEditBitItem(tr("short text"),&flags,USE_SHORTTEXT),true); Add(new cMyMenuEditBitItem(tr("long text"),&flags,USE_LONGTEXT),true); c2=Current(); @@ -1097,7 +924,6 @@ void cMenuSetupXmltv2vdrChannelMap::output(void) } else { - Add(new cMenuEditIntItem(tr("days in advance"),&days,1,daysmax),true); Add(option(tr("short text"),true),true); Add(option(tr("long text"),true),true); Add(option(tr(" merge long texts"),false),true); @@ -1116,10 +942,10 @@ void cMenuSetupXmltv2vdrChannelMap::output(void) } Add(new cMyMenuEditBitItem(tr("rating"),&flags,USE_RATING),true); + Add(new cMyMenuEditBitItem(tr("starrating"),&flags,USE_STARRATING),true); Add(new cMyMenuEditBitItem(tr("review"),&flags,USE_REVIEW),true); Add(new cMyMenuEditBitItem(tr("video"),&flags,USE_VIDEO),true); Add(new cMyMenuEditBitItem(tr("audio"),&flags,USE_AUDIO),true); - Add(new cMyMenuEditBitItem(tr("vps"),&flags,OPT_VPS),true); struct passwd *pw=getpwuid(getuid()); if (pw) @@ -1263,7 +1089,6 @@ eOSState cMenuSetupXmltv2vdrChannelMap::ProcessKey(eKeys Key) const char *oldchannel=channel; cEPGMapping *tmap=map; flags=0; - days=1; for (int i=0; i<baseplugin->EPGMappingCount();i++) { channel=baseplugin->EPGMapping(i)->ChannelName(); @@ -1315,7 +1140,6 @@ void cMenuSetupXmltv2vdrChannelMap::epgmappingreplace(cEPGMapping *newmapping) else { map->ChangeFlags(newmapping->Flags()); - map->ChangeDays(newmapping->Days()); map->ReplaceChannels(newmapping->NumChannelIDs(),newmapping->ChannelIDs()); } } @@ -1327,11 +1151,12 @@ void cMenuSetupXmltv2vdrChannelMap::Store(void) if (asprintf(&name,"channel.%s",channel)==-1) return; char *value=NULL; - if (asprintf(&value,"%i;%i;",days,flags)==-1) + if (asprintf(&value,"0;%i;",flags)==-1) { free(name); return; } + for (int i=0; i<map->NumChannelIDs(); i++) { cString ChannelID = map->ChannelIDs()[i].ToString(); @@ -1341,37 +1166,10 @@ void cMenuSetupXmltv2vdrChannelMap::Store(void) SetupStore(name,value); map->ChangeFlags(flags); - map->ChangeDays(days); epgmappingreplace(map); free(name); free(value); - - if (!baseplugin) return; - for (int i=0; i<baseplugin->EPGSourceCount(); i++) - { - cEPGSource *epgsrc=baseplugin->EPGSource(i); - if (epgsrc) - { - cEPGChannels *channellist=epgsrc->ChannelList(); - if (channellist) - { - for (int x=0; x<channellist->Count(); x++) - { - if (!strcmp(channellist->Get(x)->Name(),channel)) - { - if (epgsrc->DaysInAdvance()<days) - { - epgsrc->ChangeDaysInAdvance(days); - epgsrc->Store(); - } - break; - } - } - } - } - } - } // -------------------------------------------------------------------------------------------------------- @@ -37,14 +37,11 @@ private: int mappingBegin,mappingEnd; int sourcesBegin,sourcesEnd; int mappingEntry; - int updateEntry; int epEntry; eOSState edit(void); void generatesumchannellist(); - int exectime; - int wakeup; - int upstart; int epall; + int wakeup; public: void Output(void); static cOsdItem *NewTitle(const char *s); @@ -72,7 +69,7 @@ protected: private: cPluginXmltv2vdr *baseplugin; char country[255]; - char date[255]; + char year[255]; char originaltitle[255]; char director[255]; char actor[255]; @@ -88,6 +85,13 @@ private: char category[255]; char season[255]; char episode[255]; + char starrating[255]; + char audio[255]; + char video[255]; + char blacknwhite[255]; + char dolby[255]; + char dolbydigital[255]; + char bilingual[255]; public: cMenuSetupXmltv2vdrTextMap(cPluginXmltv2vdr *Plugin); }; @@ -101,11 +105,17 @@ private: cPluginXmltv2vdr *baseplugin; cEPGSource *epgsrc; int *sel; + time_t day; + int weekday,start; + int upstart; int days; char pin[255]; + int updateEntry; + void output(void); public: cMenuSetupXmltv2vdrChannelSource(cPluginXmltv2vdr *Plugin, cMenuSetupXmltv2vdr *Menu, int Index); ~cMenuSetupXmltv2vdrChannelSource(); + virtual eOSState ProcessKey(eKeys Key); void ClearMenu() { menu=NULL; @@ -122,13 +132,10 @@ private: cEPGMapping *map; bool hasmaps; uint flags; - int days; - int daysmax; void output(void); cString title; const char *channel; int getdaysmax(); - cOsdItem *optionN(const char *s, int num); cOsdItem *option(const char *s, bool yesno); void epgmappingreplace(cEPGMapping *newmapping); int c1,c2,c3,cm; diff --git a/source.cpp b/source.cpp new file mode 100644 index 0000000..587e949 --- /dev/null +++ b/source.cpp @@ -0,0 +1,930 @@ +/* + * source.cpp: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <vdr/timers.h> +#include <time.h> +#include <string.h> +#include <sys/ioctl.h> + +#include "source.h" +#include "extpipe.h" + +cEPGChannel::cEPGChannel(const char *Name, bool InUse) +{ + name=strdup(Name); + inuse=InUse; +} + +cEPGChannel::~cEPGChannel() +{ + if (name) free((void *) name); +} + +int cEPGChannel::Compare(const cListObject &ListObject) const +{ + cEPGChannel *epgchannel= (cEPGChannel *) &ListObject; + return strcmp(name,epgchannel->Name()); +} + +// ------------------------------------------------------------- + +cEPGExecutor::cEPGExecutor(cEPGSources *Sources) : cThread("xmltv2vdr importer") +{ + sources=Sources; +} + +void cEPGExecutor::Action() +{ + if (!sources) return; + + for (cEPGSource *epgs=sources->First(); epgs; epgs=sources->Next(epgs)) + { + if (epgs->RunItNow()) + { + int retries=0; + while (retries<=2) + { + int 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++; + } + if ((retries==2) || (!ret)) break; + } + if (retries>=2) epgs->Elog("skipping after %i retries",retries); + } + } + + int ret=1; + for (cEPGSource *epgs=sources->First(); epgs; epgs=sources->Next(epgs)) + { + if (!epgs->LastRetCode()) + { + ret=epgs->Import(*this); + break; // only import from the first successful source! + } + } + if (!ret) cSchedules::Cleanup(true); +} + +// ------------------------------------------------------------- + +cEPGSource::cEPGSource(const char *Name, const char *ConfDir, const char *EPGFile, + cEPGMappings *Maps, cTEXTMappings *Texts) +{ + if (strcmp(Name,EITSOURCE)) + { + dsyslog("xmltv2vdr: '%s' added epgsource",Name); + } + name=strdup(Name); + confdir=ConfDir; + epgfile=EPGFile; + pin=NULL; + Log=NULL; + loglen=0; + usepipe=false; + needpin=false; + running=false; + upstartdone=false; + daysinadvance=0; + exec_upstart=1; + exec_time=0; + exec_weekday=127; // Mon->Sun + lastretcode=255; + lastexec=(time_t) 0; + disabled=false; + if (strcmp(Name,EITSOURCE)) + { + ready2parse=ReadConfig(); + parse=new cParse(this, Maps); + import=new cImport(this,Maps,Texts); + Dlog("is%sready2parse",(ready2parse && parse) ? " " : " not "); + } + else + { + ready2parse=false; + disabled=true; + parse=NULL; + import=NULL; + } +} + +cEPGSource::~cEPGSource() +{ + if (strcmp(name,EITSOURCE)) + { + dsyslog("xmltv2vdr: '%s' epgsource removed",name); + } + free((void *) name); + if (pin) free((void *) pin); + if (Log) free((void *) Log); + if (parse) delete parse; + if (import) delete import; +} + +time_t cEPGSource::NextRunTime() +{ + if (disabled) return 0; // never! + if (exec_upstart) return 0; // only once! + + time_t t,now=time(NULL); + t=cTimer::SetTime(now,cTimer::TimeToInt(exec_time)); + + while ((exec_weekday & (1<<cTimer::GetWDay(t)))==0) + { + t=cTimer::IncDay(t,1); + } + + if (t<now) t=cTimer::IncDay(t,1); + return t; +} + +bool cEPGSource::RunItNow() +{ + if (disabled) return false; + struct stat statbuf; + if (stat(epgfile,&statbuf)==-1) return true; // no database? -> execute immediately + if (!statbuf.st_size) return true; // no database? -> execute immediately + + if (exec_upstart) + { + if (upstartdone) return false; + return true; + } + else + { + time_t t=time(NULL); + time_t nrt=NextRunTime(); + if (!nrt) return false; + if ((t>nrt) && t<(nrt+5)) return true; + return false; + } +} + +bool cEPGSource::ReadConfig() +{ + 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 (asprintf(&fname,"%s/%s",confdir,name)==-1) + { + Elog("out of memory"); + return false; + } + f=fopen(fname,"r+"); + if (!f) + { + if (errno!=ENOENT) + { + Elog("cannot read config file %s",fname); + free(fname); + return true; + } + /* still no config? -> ok */ + free(fname); + return 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) + { + sscanf(line,"%d;%d;%d;%d",&daysinadvance,&exec_upstart,&exec_weekday,&exec_time); + Dlog("daysinadvance=%i",daysinadvance); + Dlog("upstart=%i",exec_upstart); + if (!exec_upstart) + { + Dlog("weekdays=%s",*cTimer::PrintDay(0,exec_weekday,true)); + time_t nrt=NextRunTime(); + Dlog("nextrun on %s",ctime(&nrt)); + } + } + 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 cEPGSource::ReadOutput(char *&result, size_t &l) +{ + int ret=0; + char *fname=NULL; + if (asprintf(&fname,"%s/%s.xmltv",EPGSOURCES,name)==-1) + { + Elog("out of memory"); + return 134; + } + 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) + { + Elog("failed to read '%s'",fname); + ret=149; + free(result); + result=NULL; + } + close(fd); + free(fname); + return ret; +} + +int cEPGSource::Import(cEPGExecutor &myExecutor) +{ + Dlog("importing from db"); + return import->Process(myExecutor); +} + +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)) + { + free(Log); + Log=NULL; + loglen=0; + } + + char *cmd=NULL; + if (asprintf(&cmd,"%s %i '%s'",name,daysinadvance,pin ? pin : "")==-1) + { + Elog("out of memory"); + return 134; + } + + 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)) + { + free(cmd); + Elog("failed to open pipe"); + return 141; + } + 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; + } + } + 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("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)) + { + Dlog("parsing output"); + 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); + upstartdone=true; + lastretcode=ret; + } + + 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) +{ + 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;%i;%i;%i\n",daysinadvance,exec_upstart,exec_weekday,exec_time); + 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); +} + +void cEPGSource::add2Log(const char Prefix, const char *line) +{ + if (!line) return; + + struct tm tm; + time_t now=time(NULL); + localtime_r(&now,&tm); + char dt[30]; + strftime(dt,sizeof(dt)-1,"%H:%M ",&tm); + + 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; + } +} + +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+20+strlen(name)); + free(ptr); +} + +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+14+strlen(name)); + free(ptr); +} + +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+14+strlen(name)); + free(ptr); +} + +// ------------------------------------------------------------- + +bool cEPGSources::Exists(const char* Name) +{ + if (!Name) return false; + if (!Count()) return false; + for (int i=0; i<Count(); i++) + { + if (!strcmp(Name,Get(i)->Name())) return true; + } + return false; +} + +cEPGSource *cEPGSources::GetSource(const char* Name) +{ + if (!Name) return NULL; + if (!Count()) return NULL; + for (int i=0; i<Count(); i++) + { + if (!strcmp(Name,Get(i)->Name())) return Get(i); + } + return NULL; +} + +int cEPGSources::GetSourceIdx(const char* Name) +{ + if (!Name) return -1; + if (!Count()) return -1; + for (int i=0; i<Count(); i++) + { + if (!strcmp(Name,Get(i)->Name())) return i; + } + return -1; +} + +void cEPGSources::Remove() +{ + cEPGSource *epgs; + while ((epgs=Last())!=NULL) + { + Del(epgs); + } +} + +bool cEPGSources::RunItNow() +{ + if (!Count()) return false; + for (int i=0; i<Count(); i++) + { + if (Get(i)->RunItNow()) return true; + } + return false; +} + +time_t cEPGSources::NextRunTime() +{ + time_t next=(time_t) -1; + if (!Count()) return (time_t) 0; + + for (int i=0; i<Count(); i++) + { + time_t nrt=Get(i)->NextRunTime(); + if (nrt) + { + if (nrt<next) next=nrt; + } + } + if (next<0) next=(time_t) 0; + return next; +} + +void cEPGSources::ReadIn(const char *ConfDir, const char *EpgFile, cEPGMappings *EPGMappings, + cTEXTMappings *TextMappings, const char *SourceOrder, bool Reload) +{ + if (Reload) Remove(); + DIR *dir=opendir(EPGSOURCES); + if (!dir) return; + struct dirent *dirent; + while (dirent=readdir(dir)) + { + if (strchr(&dirent->d_name[0],'.')) continue; + if (!Exists(dirent->d_name)) + { + 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")) + { + Add(new cEPGSource(dirent->d_name,ConfDir,EpgFile,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); + } + } + } + closedir(dir); + + if (!Exists(EITSOURCE)) + { + Add(new cEPGSource(EITSOURCE,ConfDir,EpgFile,EPGMappings,TextMappings)); + } + + if (!SourceOrder) return; + char *buf=strdup(SourceOrder); + if (!buf) return; + char *saveptr; + char *pch=strtok_r(buf,",",&saveptr); + int newpos=0; + while (pch) + { + bool disable=false; + if (*pch=='-') + { + disable=true; + pch++; + } + int oldpos=GetSourceIdx(pch); + if (oldpos>-1) + { + if (disable) + { + isyslog("xmltv2vdr: disabling source '%s'",Get(oldpos)->Name()); + Get(oldpos)->Disable(); + } + if (oldpos!=newpos) Move(oldpos,newpos); + newpos++; + } + pch=strtok_r(NULL,",",&saveptr); + } + free(buf); +} + + diff --git a/source.h b/source.h new file mode 100644 index 0000000..0d1c4e8 --- /dev/null +++ b/source.h @@ -0,0 +1,200 @@ +/* + * source.h: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __source_h +#define __source_h + +#include <vdr/tools.h> + +#include "maps.h" +#include "import.h" +#include "parse.h" + +#define EPGSOURCES "/var/lib/epgsources" // NEVER (!) CHANGE THIS + +#define EITSOURCE "EIT" + +class cEPGChannel : public cListObject +{ +private: + bool inuse; + const char *name; +public: + cEPGChannel(const char *Name, bool InUse=false); + ~cEPGChannel(); + virtual int Compare(const cListObject &ListObject) const; + bool InUse() + { + return inuse; + } + void SetUsage(bool InUse) + { + inuse=InUse; + } + const char *Name() + { + return name; + } +}; + +class cEPGChannels : public cList<cEPGChannel> {}; + +class cImport; + +class cEPGSource : public cListObject +{ +private: + const char *name; + const char *confdir; + const char *epgfile; + const char *pin; + int loglen; + cParse *parse; + cImport *import; + bool ready2parse; + bool usepipe; + bool needpin; + bool running; + bool upstartdone; + bool disabled; + int daysinadvance; + int exec_upstart; + int exec_weekday; + int exec_time; + int daysmax; + time_t lastexec; + int lastretcode; + void add2Log(const char prefix, const char *line); + bool ReadConfig(); + int ReadOutput(char *&result, size_t &l); + cEPGChannels channels; +public: + cEPGSource(const char *Name,const char *ConfDir,const char *EPGFile, + cEPGMappings *Maps, cTEXTMappings *Texts); + ~cEPGSource(); + int Execute(cEPGExecutor &myExecutor); + int Import(cEPGExecutor &myExecutor); + bool RunItNow(); + time_t NextRunTime(); + void Store(void); + void ChangeChannelSelection(int *Selection); + char *Log; + bool Disabled() + { + return disabled; + } + void Disable() + { + disabled=true; + } + void Enable() + { + disabled=false; + } + cEPGChannels *ChannelList() + { + return &channels; + } + int LastRetCode() + { + return lastretcode; + } + int ExecTime() + { + return exec_time; + } + int ExecWeekDay() + { + return exec_weekday; + } + int ExecUpStart() + { + return exec_upstart; + } + int DaysMax() + { + return daysmax; + } + int DaysInAdvance() + { + return daysinadvance; + } + bool NeedPin() + { + return needpin; + } + const char *EPGFile() + { + return epgfile; + } + const char *Name() + { + return name; + } + const char *Pin() + { + return pin; + } + void ChangeExec(int UpStart, int Time, int WeekDay) + { + exec_upstart=UpStart; + exec_time=Time; + exec_weekday=WeekDay; + } + void ChangeDaysInAdvance(int NewDaysInAdvance) + { + daysinadvance=NewDaysInAdvance; + } + void ChangePin(const char *NewPin) + { + if (pin) free((void *) pin); + pin=strdup(NewPin); + } + time_t LastExecution() + { + return lastexec; + } + void Dlog(const char *format, ...); + void Elog(const char *format, ...); + void Ilog(const char *format, ...); + bool Active() + { + return running; + } +}; + +class cEPGSources : public cList<cEPGSource> +{ +public: + void ReadIn(const char *ConfDir, const char *EpgFile, cEPGMappings *EPGMappings, + cTEXTMappings *TextMappings, const char *SourceOrder, bool Reload=false); + bool RunItNow(); + time_t NextRunTime(); + bool Exists(const char *Name); + cEPGSource *GetSource(const char *Name); + int GetSourceIdx(const char *Name); + void Remove(); +}; + +class cEPGExecutor : public cThread +{ +private: + cEPGSources *sources; +public: + cEPGExecutor(cEPGSources *Sources); + bool StillRunning() + { + return Running(); + } + void Stop() + { + Cancel(3); + } + virtual void Action(); +}; + +#endif 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; diff --git a/xmltv2vdr.h b/xmltv2vdr.h index aa173fd..5a4551d 100644 --- a/xmltv2vdr.h +++ b/xmltv2vdr.h @@ -8,124 +8,86 @@ #ifndef _XMLTV2VDR_H #define _XMLTV2VDR_H -#define EPGSOURCES "/var/lib/epgsources" - #include <vdr/plugin.h> #include "maps.h" #include "parse.h" +#include "import.h" +#include "source.h" + +#if __GNUC__ > 3 +#define UNUSED(v) UNUSED_ ## v __attribute__((unused)) +#else +#define UNUSED(x) x +#endif -static const char *VERSION = "0.0.3pre"; +static const char *VERSION = "0.1.0"; static const char *DESCRIPTION = trNOOP("Imports xmltv epg into vdr"); -class cEPGChannel : public cListObject +#if VDRVERSNUM < 10726 && !EPGHANDLER +class cEpgHandler : public cListObject { -private: - bool inuse; - const char *name; public: - cEPGChannel(const char *Name, bool InUse=false); - ~cEPGChannel(); - virtual int Compare(const cListObject &ListObject) const; - bool InUse() + cEpgHandler(void) {} + virtual ~cEpgHandler() {} + virtual bool IgnoreChannel(const cChannel *UNUSED(Channel)) + { + return false; + } + virtual bool SetShortText(cEvent *UNUSED(Event), const char *UNUSED(ShortText)) { - return inuse; + return false; } - void SetUsage(bool InUse) + virtual bool SetDescription(cEvent *UNUSED(Event), const char *UNUSED(Description)) { - inuse=InUse; + return false; } - const char *Name() + virtual bool SetContents(cEvent *UNUSED(Event), uchar *UNUSED(Contents)) { - return name; + return false; + } + virtual bool SetParentalRating(cEvent *UNUSED(Event), int UNUSED(ParentalRating)) + { + return false; } }; +#endif -class cEPGChannels : public cList<cEPGChannel> {}; +class cEPGSources; -class cEPGExecutor; +class cImport; -class cEPGSource : public cListObject +class cEPGHandler : public cEpgHandler { private: - const char *name; - const char *confdir; - const char *pin; - int loglen; - cParse *parse; - bool ready2parse; - bool usepipe; - bool needpin; - bool running; - int daysinadvance; - int daysmax; - time_t lastexec; - void add2Log(const char prefix, const char *line); - bool ReadConfig(); - int ReadOutput(char *&result, size_t &l); - cEPGChannels channels; + const char *epgfile; + cEPGMappings *maps; + cEPGSources *sources; + cImport *import; + bool epall; public: - cEPGSource(const char *Name,const char *ConfDir,cEPGMappings *Maps,cTEXTMappings *Texts); - ~cEPGSource(); - int Execute(cEPGExecutor &myExecutor); - void Store(void); - void ChangeChannelSelection(int *Selection); - char *Log; - cEPGChannels *ChannelList() - { - return &channels; - } - int DaysMax() - { - return daysmax; - } - int DaysInAdvance() - { - return daysinadvance; - } - bool NeedPin() - { - return needpin; - } - const char *Name() - { - return name; - } - const char *Pin() - { - return pin; - } - void ChangeDaysInAdvance(int NewDaysInAdvance) + cEPGHandler(const char *EpgFile, cEPGSources *Sources, cEPGMappings *Maps, cTEXTMappings *Texts); + void SetEPAll(bool Value) { - daysinadvance=NewDaysInAdvance; - } - void ChangePin(const char *NewPin) - { - if (pin) free((void *) pin); - pin=strdup(NewPin); - } - time_t LastExecution() - { - return lastexec; - } - void Dlog(const char *format, ...); - void Elog(const char *format, ...); - void Ilog(const char *format, ...); - bool Active() - { - return running; + epall=Value; } + virtual ~cEPGHandler(); + virtual bool IgnoreChannel(const cChannel *Channel); + virtual bool SetShortText(cEvent *Event, const char *ShortText); + virtual bool SetDescription(cEvent *Event, const char *Description); + virtual bool SetContents(cEvent *Event, uchar *Contents); + virtual bool SetParentalRating(cEvent *Event, int ParentalRating); }; -class cEPGSources : public cList<cEPGSource> {}; - -class cEPGExecutor : public cThread +class cEPGTimer : public cThread { -private: - cEPGSources *sources; - cTEXTMappings *textmappings; - bool epall; -public: - cEPGExecutor(cEPGSources *Sources); + private: + const char *epgfile; + cEPGSources *sources; + cEPGMappings *maps; + cImport *import; + public: + cEPGTimer(const char *EpgFile, cEPGSources *Sources, cEPGMappings *Maps, + cTEXTMappings *Texts); bool StillRunning() { return Running(); @@ -134,45 +96,53 @@ public: { Cancel(3); } - void SetOptions(bool EPAll, cTEXTMappings *TextMappings) { - epall=EPAll; - textmappings=TextMappings; - } - virtual void Action(); + virtual void Action(); }; class cPluginXmltv2vdr : public cPlugin { private: + cEPGHandler *epghandler; + cEPGTimer *epgtimer; cEPGExecutor epgexecutor; cEPGMappings epgmappings; cEPGSources epgsources; cTEXTMappings textmappings; - void removeepgsources(); - void removeepgmappings(); - void removetextmappings(); - bool epgsourceexists(const char *name); - int exectime; - time_t exectime_t,last_exectime_t; + time_t last_housetime_t; + time_t last_maintime_t; + time_t last_epcheck_t; char *confdir; + char *epgfile; + char *srcorder; bool epall; + bool wakeup; public: - int ExecTime() + void SetEPAll(bool Value) { - return exectime; + epall=Value; + if (epghandler) epghandler->SetEPAll(Value); } - void SetExecTime(int ExecTime); - bool UpStart; - bool WakeUp; - void SetEPAll(bool Value) { - epall=Value; - epgexecutor.SetOptions(epall,&textmappings); + bool EPAll() + { + return epall; + } + void SetWakeUp(bool Value) + { + wakeup=Value; } - bool EPAll() { return epall; } - void ReadInEPGSources(bool Reload=false); + bool WakeUp() + { + return wakeup; + } + void ReadInEPGSources(bool Reload=false) + { + epgsources.ReadIn(confdir,epgfile,&epgmappings,&textmappings,srcorder,Reload); + } + bool EPGSourceMove(int From, int To); int EPGSourceCount() { - return epgsources.Count(); + if (!epgsources.Count()) return 0; + return epgsources.Count()-1; } cEPGSource *EPGSource(int Index) { @@ -186,12 +156,18 @@ public: { return epgmappings.Get(Index); } - cEPGMapping *EPGMapping(const char *ChannelName); + cEPGMapping *EPGMapping(const char *ChannelName) + { + return epgmappings.GetMap(ChannelName); + } void EPGMappingAdd(cEPGMapping *Map) { epgmappings.Add(Map); } - cTEXTMapping *TEXTMapping(const char *Name); + cTEXTMapping *TEXTMapping(const char *Name) + { + return textmappings.GetMap(Name); + } void TEXTMappingAdd(cTEXTMapping *TextMap) { textmappings.Add(TextMap); |