summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY7
-rw-r--r--Makefile8
-rw-r--r--dist/patches/README5
-rw-r--r--dist/patches/vdr-1.6.0-3.epghandler.patch.gzbin0 -> 11544 bytes
-rw-r--r--event.cpp402
-rw-r--r--event.h221
-rw-r--r--import.cpp1222
-rw-r--r--import.h77
-rw-r--r--maps.cpp116
-rw-r--r--maps.h32
-rw-r--r--parse.cpp1179
-rw-r--r--parse.h178
-rw-r--r--po/de_DE.po155
-rw-r--r--po/it_IT.po152
-rw-r--r--setup.cpp674
-rw-r--r--setup.h23
-rw-r--r--source.cpp930
-rw-r--r--source.h200
-rw-r--r--xmltv2vdr.cpp1107
-rw-r--r--xmltv2vdr.h208
20 files changed, 4228 insertions, 2668 deletions
diff --git a/HISTORY b/HISTORY
index 2a8d115..5b03697 100644
--- a/HISTORY
+++ b/HISTORY
@@ -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
diff --git a/Makefile b/Makefile
index 6befb58..0b9b215 100644
--- a/Makefile
+++ b/Makefile
@@ -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
new file mode 100644
index 0000000..0323d43
--- /dev/null
+++ b/dist/patches/vdr-1.6.0-3.epghandler.patch.gz
Binary files differ
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();
+}
diff --git a/event.h b/event.h
new file mode 100644
index 0000000..082e8c1
--- /dev/null
+++ b/event.h
@@ -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
diff --git a/maps.cpp b/maps.cpp
index be92a4c..0daf19e 100644
--- a/maps.cpp
+++ b/maps.cpp
@@ -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&copy)
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--;
}
diff --git a/maps.h b/maps.h
index 71458ef..b49afdf 100644
--- a/maps.h
+++ b/maps.h
@@ -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
diff --git a/parse.cpp b/parse.cpp
index 01d5701..b7e348b 100644
--- a/parse.cpp
+++ b/parse.cpp
@@ -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);
+ }
}
diff --git a/parse.h b/parse.h
index 9e20ff2..4ba6b31 100644
--- a/parse.h
+++ b/parse.h
@@ -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"
+
diff --git a/setup.cpp b/setup.cpp
index daf4019..ca0fe44 100644
--- a/setup.cpp
+++ b/setup.cpp
@@ -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;
- }
- }
- }
- }
- }
-
}
// --------------------------------------------------------------------------------------------------------
diff --git a/setup.h b/setup.h
index 53dda91..26a42ec 100644
--- a/setup.h
+++ b/setup.h
@@ -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);