summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY6
-rw-r--r--Makefile120
-rw-r--r--README17
-rw-r--r--extpipe.cpp131
-rw-r--r--extpipe.h23
-rw-r--r--maps.cpp212
-rw-r--r--maps.h107
-rw-r--r--parse.cpp647
-rw-r--r--parse.h146
-rw-r--r--po/de_DE.po164
-rw-r--r--setup.cpp1034
-rw-r--r--setup.h119
-rw-r--r--xmltv2vdr.cpp641
-rw-r--r--xmltv2vdr.h201
14 files changed, 3568 insertions, 0 deletions
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..2a8d115
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,6 @@
+VDR Plugin 'xmltv2vdr' Revision History
+---------------------------------------
+
+2010-12-05: Version 0.0.1
+
+- Initial revision.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6e394be
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,120 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+# IMPORTANT: the presence of this macro is important for the Make.config
+# file. So it must be defined, even if it is not used here!
+#
+PLUGIN = xmltv2vdr
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).h | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -fPIC -g -O0 -Wall -Wextra -pedantic -Woverloaded-virtual -Wno-parentheses
+PKG-CONFIG ?= pkg-config
+
+### The directory environment:
+
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR's plugin API (taken from VDR's "config.h"):
+
+APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+PKG-LIBS += libxml-2.0
+PKG-INCLUDES += libxml-2.0
+
+INCLUDES += -I$(VDRDIR)/include
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+INCLUDES += $(shell $(PKG-CONFIG) --cflags $(PKG-INCLUDES))
+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
+
+### The main target:
+
+all: libvdr-$(PLUGIN).so i18n
+
+### Implicit rules:
+
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.cpp) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+LOCALEDIR = $(VDRDIR)/locale
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.cpp)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<see README>' -o $@ $^
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ @mkdir -p $(dir $@)
+ cp $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmsgs) $(I18Npot)
+
+### Targets:
+
+#parse: parse.o
+# $(CXX) $(CXXFLAGS) parse.o $(LIBS) -o parse
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
+ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a *.cpp *.h HISTORY Makefile README po $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) --exclude debian --exclude CVS --exclude .svn $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot
diff --git a/README b/README
new file mode 100644
index 0000000..10fe37d
--- /dev/null
+++ b/README
@@ -0,0 +1,17 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Jochen Dolze <vdr@dolze.de>
+
+Project's homepage: http://projects.vdr-developer.org/projects/plg-xmltv2vdr
+
+Latest version available at: http://projects.vdr-developer.org/projects/plg-xmltv2vdr/files
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+See the file COPYING for more information.
+
+Description:
+
+Add epg info from epg sources into vdr
diff --git a/extpipe.cpp b/extpipe.cpp
new file mode 100644
index 0000000..c596226
--- /dev/null
+++ b/extpipe.cpp
@@ -0,0 +1,131 @@
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+#include "extpipe.h"
+
+
+
+cExtPipe::cExtPipe(void)
+{
+ pid = -1;
+ f = NULL;
+}
+
+cExtPipe::~cExtPipe()
+{
+ int status;
+ Close(status);
+}
+
+bool cExtPipe::Open(const char *Command, const char *Mode)
+{
+ int fd[2];
+
+ if (pipe(fd) < 0)
+ {
+ LOG_ERROR;
+ return false;
+ }
+ if ((pid = fork()) < 0) // fork failed
+ {
+ LOG_ERROR;
+ close(fd[0]);
+ close(fd[1]);
+ return false;
+ }
+
+ const char *mode = "w";
+ int iopipe = 0;
+
+ if (pid > 0) // parent process
+ {
+ if (strcmp(Mode, "r") == 0)
+ {
+ mode = "r";
+ iopipe = 1;
+ }
+ close(fd[iopipe]);
+ if ((f = fdopen(fd[1 - iopipe], mode)) == NULL)
+ {
+ LOG_ERROR;
+ close(fd[1 - iopipe]);
+ }
+ return f != NULL;
+ }
+ else // child process
+ {
+ int iofd = STDOUT_FILENO;
+ if (strcmp(Mode, "w") == 0)
+ {
+ mode = "r";
+ iopipe = 1;
+ iofd = STDIN_FILENO;
+ }
+ close(fd[iopipe]);
+ if (dup2(fd[1 - iopipe], iofd) == -1) // now redirect
+ {
+ LOG_ERROR;
+ close(fd[1 - iopipe]);
+ _exit(-1);
+ }
+ else
+ {
+ int MaxPossibleFileDescriptors = getdtablesize();
+ for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
+ close(i); //close all dup'ed filedescriptors
+ if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1)
+ {
+ LOG_ERROR_STR(Command);
+ close(fd[1 - iopipe]);
+ _exit(-1);
+ }
+ }
+ _exit(0);
+ }
+}
+
+int cExtPipe::Close(int &status)
+{
+ int ret = -1;
+
+ if (f)
+ {
+ fclose(f);
+ f = NULL;
+ }
+
+ if (pid > 0)
+ {
+ int i = 5;
+ while (i > 0)
+ {
+ ret = waitpid(pid, &status, WNOHANG);
+ if (ret < 0)
+ {
+ if (errno != EINTR && errno != ECHILD)
+ {
+ LOG_ERROR;
+ break;
+ }
+ }
+ else if (ret == pid)
+ break;
+ i--;
+ cCondWait::SleepMs(100);
+ }
+ if (!i)
+ {
+ kill(pid, SIGKILL);
+ ret = -1;
+ }
+ else if (ret == -1 || !WIFEXITED(status))
+ ret = -1;
+ pid = -1;
+ }
+
+ return ret;
+}
diff --git a/extpipe.h b/extpipe.h
new file mode 100644
index 0000000..ebca65d
--- /dev/null
+++ b/extpipe.h
@@ -0,0 +1,23 @@
+#ifndef _EXTPIPE_H
+#define _EXTPIPE_H
+
+#include <sys/types.h>
+#include <stdio.h>
+
+class cExtPipe
+{
+private:
+ pid_t pid;
+ FILE *f;
+public:
+ cExtPipe(void);
+ ~cExtPipe();
+ operator FILE* ()
+ {
+ return f;
+ }
+ bool Open(const char *Command, const char *Mode);
+ int Close(int &status);
+};
+
+#endif \ No newline at end of file
diff --git a/maps.cpp b/maps.cpp
new file mode 100644
index 0000000..884a23f
--- /dev/null
+++ b/maps.cpp
@@ -0,0 +1,212 @@
+/*
+ * maps.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "maps.h"
+#include <limits.h>
+
+cTEXTMapping::cTEXTMapping(const char *Name, const char *Value)
+{
+ name=strdup(Name);
+ value=strdup(Value);
+}
+
+cTEXTMapping::~cTEXTMapping()
+{
+ if (name) free((void *) name);
+ if (value) free((void *) value);
+}
+
+void cTEXTMapping::ChangeValue(const char *Value)
+{
+ if (value) free((void *) value);
+ value=strdup(Value);
+}
+
+// --------------------------------------------------------------------------------------------------------
+
+cEPGMapping::cEPGMapping(const char *ChannelName, const char *Flags_Days_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)
+ {
+ char *flags_days_p=(char *) strdup(Flags_Days_and_Channels);
+ if (!flags_days_p) return;
+
+ char *flags_p=strchr(flags_days_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)
+ {
+ *channels_p=0;
+ channels_p++;
+ flags=atoi(flags_p);
+ addchannels(channels_p);
+ }
+ }
+ free(flags_days_p);
+ }
+}
+
+cEPGMapping::~cEPGMapping()
+{
+ if (channelname) free((void *) channelname);
+ if (channelids) free(channelids);
+}
+
+cEPGMapping::cEPGMapping(cEPGMapping&copy)
+{
+ channelname=strdup(copy.channelname);
+ channelids=NULL;
+ numchannelids=0;
+ if (copy.numchannelids>0)
+ {
+ channelids=(tChannelID *) malloc((copy.numchannelids+1)*sizeof(tChannelID));
+ if (!channelids) return;
+ for (int i=0; i<copy.numchannelids; i++)
+ channelids[i]=copy.channelids[i];
+ numchannelids=copy.numchannelids;
+ }
+ flags=copy.flags;
+ days=copy.days;
+}
+
+int cEPGMapping::compare(const void *a, const void *b)
+{
+ tChannelID *v1=(tChannelID *) a;
+ tChannelID *v2=(tChannelID *) b;
+ int num1=0,num2=0;
+ if (*v1==tChannelID::InvalidID)
+ {
+ num1=INT_MAX;
+ }
+ else
+ {
+ cChannel *c1=Channels.GetByChannelID(*v1);
+ if (c1) num1=c1->Number();
+ }
+ if (*v2==tChannelID::InvalidID)
+ {
+ num2=INT_MAX;
+ }
+ else
+ {
+ cChannel *c2=Channels.GetByChannelID(*v2);
+ if (c2) num2=c2->Number();
+ }
+ if (num1>num2) return 1;
+ else return -1;
+}
+
+void cEPGMapping::addchannels(const char *channels)
+{
+ char *tmp=(char *) strdup(channels);
+ if (!tmp) return;
+
+ char *token,*str1,*saveptr;
+ str1=tmp;
+
+ while (token=strtok_r(str1,";",&saveptr))
+ {
+ tChannelID ChannelID=tChannelID::FromString(token);
+ if (!(ChannelID==tChannelID::InvalidID))
+ {
+ channelids=(tChannelID *) realloc(channelids,(numchannelids+1)*sizeof(tChannelID));
+ if (!channelids)
+ {
+ free(tmp);
+ return;
+ }
+ channelids[numchannelids]=ChannelID;
+ numchannelids++;
+ }
+ str1=NULL;
+ }
+ free(tmp);
+}
+
+void cEPGMapping::AddChannel(int ChannelNumber)
+{
+ cChannel *chan=Channels.GetByNumber(ChannelNumber);
+ if (chan)
+ {
+ bool found=false;
+ for (int i=0; i<numchannelids; i++)
+ {
+ if (channelids[i]==chan->GetChannelID())
+ {
+ found=true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ channelids=(tChannelID *) realloc(channelids,(numchannelids+1)*sizeof(tChannelID));
+ if (!channelids) return;
+ channelids[numchannelids]=chan->GetChannelID();
+ numchannelids++;
+ qsort(channelids,numchannelids,sizeof(tChannelID),compare);
+ }
+ }
+}
+
+void cEPGMapping::ReplaceChannels(int NumChannelIDs, tChannelID *ChannelIDs)
+{
+ if (NumChannelIDs<0) return;
+ free(channelids);
+ channelids=NULL;
+ numchannelids=0;
+ if (!NumChannelIDs) return;
+ if (!ChannelIDs) return;
+
+ for (int i=0; i<NumChannelIDs; i++)
+ {
+ channelids=(tChannelID *) realloc(channelids,(numchannelids+1)*sizeof(tChannelID));
+ if (!channelids) return;
+ channelids[numchannelids]=ChannelIDs[i];
+ numchannelids++;
+ qsort(channelids,numchannelids,sizeof(tChannelID),compare);
+ }
+}
+
+void cEPGMapping::RemoveChannel(int ChannelNumber)
+{
+ if (!ChannelNumber) return;
+ cChannel *chan=Channels.GetByNumber(ChannelNumber);
+ if (!chan) return;
+
+ bool found=false;
+ int i;
+ for (i=0; i<numchannelids; i++)
+ {
+ if (channelids[i]==chan->GetChannelID())
+ {
+ found=true;
+ break;
+ }
+ }
+ if (found)
+ {
+ channelids[i]=tChannelID::InvalidID;
+ qsort(channelids,numchannelids,sizeof(tChannelID),compare);
+ numchannelids--;
+ }
+}
diff --git a/maps.h b/maps.h
new file mode 100644
index 0000000..82a9f8a
--- /dev/null
+++ b/maps.h
@@ -0,0 +1,107 @@
+/*
+ * maps.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _MAPS_H
+#define _MAPS_H
+
+#include <vdr/channels.h>
+
+// Usage field definition
+
+// Bit 0- 5 DAYS in advance +1 -> Range 1-32
+// Bit 6-23 USE_ flags
+// Bit 24-30 OPT_ flags
+// Bit 31 always zero
+
+#define USE_NOTHING 0
+
+#define USE_SHORTTEXT 0x1
+#define USE_LONGTEXT 0x2
+#define USE_COUNTRYDATE 0x4
+#define USE_ORIGTITLE 0x8
+#define USE_CATEGORY 0x10
+#define USE_CREDITS 0x20
+#define USE_RATING 0x40
+#define USE_REVIEW 0x80
+#define USE_VIDEO 0x100
+#define USE_AUDIO 0x200
+
+#define OPT_MERGELTEXT 0x10000000
+#define OPT_VPS 0x20000000
+#define OPT_APPEND 0x40000000
+
+class cTEXTMapping : public cListObject
+{
+private:
+ const char *name;
+ const char *value;
+public:
+ cTEXTMapping(const char *Name, const char *Value);
+ ~cTEXTMapping();
+ void ChangeValue(const char *Value);
+ const char *Name(void)
+ {
+ return name;
+ }
+ const char *Value(void)
+ {
+ return value;
+ }
+};
+
+class cTEXTMappings : public cList<cTEXTMapping> {};
+
+class cEPGMapping : public cListObject
+{
+private:
+ static int compare(const void *a, const void *b);
+ const char *channelname;
+ tChannelID *channelids;
+ int numchannelids;
+ int flags;
+ int days;
+ void addchannels(const char *channels);
+public:
+ cEPGMapping(const char *ChannelName, const char *Flags_and_Mappings);
+ ~cEPGMapping();
+ cEPGMapping(cEPGMapping&copy);
+ void ChangeFlags(int NewFlags)
+ {
+ flags=NewFlags;
+ }
+ void ChangeDays(int NewDays)
+ {
+ days=NewDays;
+ }
+ void ReplaceChannels(int NumChannelIDs, tChannelID *ChannelIDs);
+ void AddChannel(int ChannelNumber);
+ void RemoveChannel(int ChannelNumber);
+ int Flags()
+ {
+ return flags;
+ }
+ int Days()
+ {
+ return days;
+ }
+ const char *ChannelName()
+ {
+ return channelname;
+ }
+ int NumChannelIDs()
+ {
+ return numchannelids;
+ }
+ tChannelID *ChannelIDs()
+ {
+ return channelids;
+ }
+};
+
+class cEPGMappings : public cList<cEPGMapping> {};
+
+#endif
diff --git a/parse.cpp b/parse.cpp
new file mode 100644
index 0000000..747567d
--- /dev/null
+++ b/parse.cpp
@@ -0,0 +1,647 @@
+/*
+ * parse.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "parse.h"
+
+extern char *strcatrealloc(char *dest, const char *src);
+
+void cXMLTVEvent::SetTitle(const char *Title)
+{
+ title = strcpyrealloc(title, Title);
+ title = compactspace(title);
+}
+
+void cXMLTVEvent::SetOrigTitle(const char *OrigTitle)
+{
+ origtitle = strcpyrealloc(origtitle, OrigTitle);
+ origtitle = compactspace(origtitle);
+}
+
+void cXMLTVEvent::SetShortText(const char *ShortText)
+{
+ shorttext=strcpyrealloc(shorttext,ShortText);
+ shorttext=compactspace(shorttext);
+}
+
+void cXMLTVEvent::SetDescription(const char *Description)
+{
+ description = strcpyrealloc(description, Description);
+ description = compactspace(description);
+ if (description)
+ {
+ if (description[strlen(description)-1]!='\n')
+ description=strcatrealloc(description,"\n");
+ }
+}
+
+bool cXMLTVEvent::AddDescription(const char *Name, const char *Value)
+{
+ description = strcatrealloc(description,Name);
+ description = strcatrealloc(description,": ");
+ description = strcatrealloc(description,Value);
+ description = strcatrealloc(description,"\n");
+ return (description);
+}
+
+bool cXMLTVEvent::AddDescription(const char *Name, int Value)
+{
+ char *value=NULL;
+ if (asprintf(&value,"%i",Value)==-1) return false;
+ description = strcatrealloc(description,Name);
+ description = strcatrealloc(description,": ");
+ description = strcatrealloc(description,value);
+ description = strcatrealloc(description,"\n");
+ free(value);
+ return (description);
+}
+
+void cXMLTVEvent::SetCountry(const char *Country)
+{
+ country=strcpyrealloc(country, Country);
+ country=compactspace(country);
+}
+
+void cXMLTVEvent::SetReview(const char *Review)
+{
+ review=strcpyrealloc(review, Review);
+ review=compactspace(review);
+}
+
+void cXMLTVEvent::SetRating(const char *System, const char *Rating)
+{
+ system=strcpyrealloc(system, System);
+ system=compactspace(system);
+
+ rating=strcpyrealloc(rating, Rating);
+ rating=compactspace(rating);
+}
+
+void cXMLTVEvent::Clear()
+{
+ if (title)
+ {
+ free(title);
+ title=NULL;
+ }
+ if (shorttext)
+ {
+ free(shorttext);
+ shorttext=NULL;
+ }
+ if (description)
+ {
+ free(description);
+ description=NULL;
+ }
+ if (country)
+ {
+ free(country);
+ country=NULL;
+ }
+ if (system)
+ {
+ free(system);
+ system=NULL;
+ }
+ if (review)
+ {
+ free(review);
+ review=NULL;
+ }
+ if (rating)
+ {
+ free(rating);
+ rating=NULL;
+ }
+ if (origtitle)
+ {
+ free(origtitle);
+ origtitle=NULL;
+ }
+ year=0;
+ vps= (time_t) 0;
+ starttime = 0;
+ duration = 0;
+ eventid=0;
+}
+
+cXMLTVEvent::cXMLTVEvent()
+{
+ title=NULL;
+ shorttext=NULL;
+ description=NULL;
+ country=NULL;
+ system=NULL;
+ rating=NULL;
+ review=NULL;
+ origtitle=NULL;
+ Clear();
+}
+
+cXMLTVEvent::~cXMLTVEvent()
+{
+ Clear();
+}
+
+// -------------------------------------------------------
+
+time_t cParse::ConvertXMLTVTime2UnixTime(char *xmltvtime)
+{
+ time_t offset=0;
+ if (!xmltvtime) return (time_t) 0;
+ char *withtz=strchr(xmltvtime,' ');
+ int len;
+ if (withtz)
+ {
+ len=strlen(xmltvtime)-(withtz-xmltvtime)-1;
+ *withtz=':';
+ if ((withtz[1]=='+') || (withtz[1]=='-'))
+ {
+ if (len==5)
+ {
+ int val=atoi(&withtz[1]);
+ int h=val/100;
+ int m=val-(h*100);
+ offset=h*3600+m*60;
+ setenv("TZ",":UTC",1);
+ }
+ else
+ {
+ setenv("TZ",":UTC",1);
+ }
+ }
+ else
+ {
+ if (len>2)
+ {
+ setenv("TZ",withtz,1);
+ }
+ else
+ {
+ setenv("TZ",":UTC",1);
+ }
+ }
+ }
+ else
+ {
+ withtz=&xmltvtime[strlen(xmltvtime)];
+ setenv("TZ",":UTC",1);
+ }
+ tzset();
+
+ len=withtz-xmltvtime;
+ if (len<4)
+ {
+ unsetenv("TZ");
+ tzset();
+ return (time_t) 0;
+ }
+ len-=2;
+ char fmt[]="%Y%m%d%H%M%S";
+ fmt[len]=0;
+
+ struct tm tm;
+ memset(&tm,0,sizeof(tm));
+ if (!strptime(xmltvtime,fmt,&tm))
+ {
+ unsetenv("TZ");
+ tzset();
+ return (time_t) 0;
+ }
+ if (tm.tm_mday==0) tm.tm_mday=1;
+ time_t ret=mktime(&tm);
+ ret-=offset;
+ unsetenv("TZ");
+ tzset();
+ return ret;
+}
+
+cEvent *cParse::SearchEvent(cSchedule* schedule, cXMLTVEvent *xevent)
+{
+ if (!xevent) return NULL;
+ if (!xevent->Duration()) return NULL;
+ if (!xevent->StartTime()) return NULL;
+ if (!xevent->Title()) return NULL;
+ cEvent *f=NULL;
+
+ time_t start=xevent->StartTime();
+ if (!xevent->EventID())
+ {
+ xevent->SetEventID(DoSum((u_long) start,xevent->Title(),strlen(xevent->Title())));
+ }
+
+ // try to find an event,
+ // 1st with our own EventID
+ if (xevent->EventID()) f=(cEvent *) schedule->GetEvent((tEventID) xevent->EventID());
+ if (f) return f;
+ // 2nd with StartTime
+ f=(cEvent *) schedule->GetEvent((tEventID) 0,start);
+ if (f) return f;
+ // 3rd with StartTime +/- WaitTime
+ int maxdiff=INT_MAX;
+ int eventTimeDiff=xevent->Duration()/4;
+ if (eventTimeDiff<600) eventTimeDiff=600;
+
+ for (cEvent *p = schedule->Events()->First(); p; p = schedule->Events()->Next(p))
+ {
+ if (!strcmp(p->Title(),xevent->Title()))
+ {
+ // found event with same title
+ int diff=abs((int) difftime(p->StartTime(),start));
+ if (diff<=eventTimeDiff)
+ {
+ if (diff<=maxdiff)
+ {
+ f=p;
+ maxdiff=diff;
+ }
+ }
+ }
+ }
+ return f;
+}
+
+bool cParse::PutEvent(cSchedule* schedule, cEvent *event, cXMLTVEvent *xevent, cEPGMapping *map)
+{
+ if (!schedule) return false;
+ if (!xevent) return false;
+ if (!map) return false;
+
+ time_t start;
+ if (!event)
+ {
+ if ((map->Flags() & OPT_APPEND)==OPT_APPEND)
+ {
+ start=xevent->StartTime();
+ event=new cEvent(xevent->EventID());
+ if (!event) return false;
+ event->SetStartTime(start);
+ event->SetDuration(xevent->Duration());
+ event->SetTitle(xevent->Title());
+ event->SetShortText(xevent->ShortText());
+ event->SetDescription(xevent->Description());
+ event->SetVersion(0);
+ schedule->AddEvent(event);
+ dsyslog("xmltv2vdr: '%s' adding event '%s' @%s",name,xevent->Title(),ctime(&start));
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (event->TableID()==0) return true;
+ start=event->StartTime();
+ dsyslog("xmltv2vdr: '%s' changing event '%s' @%s",name,event->Title(),ctime(&start));
+ }
+ if ((map->Flags() & USE_SHORTTEXT)==USE_SHORTTEXT)
+ {
+ event->SetShortText(xevent->ShortText());
+ }
+ if ((map->Flags() & USE_LONGTEXT)==USE_LONGTEXT)
+ {
+ event->SetDescription(xevent->Description());
+ }
+ else
+ {
+ xevent->SetDescription(event->Description());
+ }
+ bool addExt=false;
+ if ((map->Flags() & USE_COUNTRYDATE)==USE_COUNTRYDATE)
+ {
+ if (xevent->Country())
+ {
+ cTEXTMapping *text=TEXTMapping("country");
+ if (text) addExt=xevent->AddDescription(text->Value(),xevent->Country());
+ }
+
+ if (xevent->Year())
+ {
+ cTEXTMapping *text=TEXTMapping("date");
+ if (text) addExt=xevent->AddDescription(text->Value(),xevent->Year());
+ }
+ }
+ if (((map->Flags() & USE_ORIGTITLE)==USE_ORIGTITLE) && (xevent->OrigTitle()))
+ {
+ cTEXTMapping *text;
+ text=TEXTMapping("originaltitle");
+ if (text) addExt=xevent->AddDescription(text->Value(),xevent->OrigTitle());
+ }
+ if (((map->Flags() & USE_RATING)==USE_RATING) && (xevent->Rating()) && (xevent->RatingSystem()))
+ {
+ addExt=xevent->AddDescription(xevent->RatingSystem(),xevent->Rating());
+ }
+ if (((map->Flags() & USE_REVIEW)==USE_REVIEW) && (xevent->Review()))
+ {
+ cTEXTMapping *text;
+ text=TEXTMapping("review");
+ if (text) addExt=xevent->AddDescription(text->Value(),xevent->Review());
+ }
+ if (addExt) event->SetDescription(xevent->Description());
+ event->SetTableID(0); // prevent EIT EPG to update this event
+ return true;
+}
+
+u_long cParse::DoSum(u_long sum, const char *buf, int nBytes)
+{
+ int nleft=nBytes;
+ u_short *w = (u_short*)buf;
+
+ while (nleft > 1)
+ {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ if (nleft == 1)
+ {
+ u_short answer = 0;
+ *(u_char*)(&answer) = *(u_char*)w;
+ sum += answer;
+ }
+ return sum;
+}
+
+bool cParse::FetchEvent(xmlNodePtr enode)
+{
+ char *slang=getenv("LANG");
+ xmlNodePtr node=enode->xmlChildrenNode;
+ while (node)
+ {
+ if (node->type==XML_ELEMENT_NODE)
+ {
+ if ((!xmlStrcasecmp(node->name, (const xmlChar *) "title")))
+ {
+ xmlChar *lang=xmlGetProp(node,(const xmlChar *) "lang");
+ if (lang && slang && !xmlStrncasecmp(lang, (const xmlChar *) slang,2))
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetTitle(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ xmlFree(lang);
+ }
+ else
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetOrigTitle(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "sub-title")))
+ {
+ // what to do with attribute lang?
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetShortText(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "desc")))
+ {
+ // what to do with attribute lang?
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetDescription(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "country")))
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetCountry(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "date")))
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetYear(atoi((const char *) content));
+ xmlFree(content);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "category")))
+ {
+ // attribute lang
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "credits")))
+ {
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "rating")))
+ {
+ xmlChar *system=xmlGetProp(node,(const xmlChar *) "system");
+ if (system)
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ const char *crating=strdup(conv->Convert((const char *) content));
+ const char *csystem=strdup(conv->Convert((const char *) system));
+ xevent.SetRating(csystem,crating);
+ if (crating) free((void *) crating);
+ if (csystem) free((void *) csystem);
+ xmlFree(content);
+ }
+ xmlFree(system);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "review")))
+ {
+ xmlChar *type=xmlGetProp(node,(const xmlChar *) "type");
+ if (type && !xmlStrcasecmp(type, (const xmlChar *) "text"))
+ {
+ xmlChar *content=xmlNodeListGetString(node->doc,node->xmlChildrenNode,1);
+ if (content)
+ {
+ xevent.SetReview(conv->Convert((const char *) content));
+ xmlFree(content);
+ }
+ xmlFree(type);
+ }
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "video")))
+ {
+ }
+ else if ((!xmlStrcasecmp(node->name, (const xmlChar *) "audio")))
+ {
+ }
+ else
+ {
+ esyslog("xmltv2vdr: '%s' unknown element %s, please report!",name,node->name);
+ }
+ }
+ node=node->next;
+ }
+ return true;
+}
+
+cTEXTMapping *cParse::TEXTMapping(const char *Name)
+{
+ if (!texts->Count()) return NULL;
+ for (cTEXTMapping *textmap=texts->First(); textmap; textmap=texts->Next(textmap))
+ {
+ if (!strcmp(textmap->Name(),Name)) return textmap;
+ }
+ return NULL;
+}
+
+cEPGMapping *cParse::EPGMapping(const char *ChannelName)
+{
+ if (!maps->Count()) return NULL;
+ for (cEPGMapping *map=maps->First(); map; map=maps->Next(map))
+ {
+ if (!strcmp(map->ChannelName(),ChannelName)) return map;
+ }
+ return NULL;
+}
+
+bool cParse::Process(char *buffer, int bufsize)
+{
+ if (!buffer) return false;
+ if (!bufsize) return false;
+
+ xmlDocPtr xmltv;
+ xmltv=xmlReadMemory(buffer,bufsize,NULL,NULL,0);
+ if (!xmltv) return false;
+
+ xmlNodePtr rootnode=xmlDocGetRootElement(xmltv);
+ if (!rootnode)
+ {
+ xmlFreeDoc(xmltv);
+ return false;
+ }
+
+ time_t begin=time(NULL);
+ xmlNodePtr node=rootnode->xmlChildrenNode;
+ while (node)
+ {
+ if (node->type==XML_ELEMENT_NODE)
+ {
+ if ((!xmlStrcasecmp(node->name, (const xmlChar *) "programme")))
+ {
+ xmlChar *channelid=xmlGetProp(node,(const xmlChar *) "channelid");
+ cEPGMapping *map=NULL;
+ if (channelid && (map=EPGMapping((const char *) channelid)))
+ {
+ time_t end=begin+86000*map->Days();
+ xmlChar *start,*stop;
+ time_t starttime=(time_t) 0;
+ time_t stoptime=(time_t) 0;
+ start=xmlGetProp(node,(const xmlChar *) "start");
+ if (start)
+ {
+ starttime=ConvertXMLTVTime2UnixTime((char *) start);
+ if (starttime)
+ {
+ stop=xmlGetProp(node,(const xmlChar *) "stop");
+ if (stop)
+ {
+ stoptime=ConvertXMLTVTime2UnixTime((char *) stop);
+ xmlFree(stop);
+ }
+ }
+ xmlFree(start);
+ }
+
+ if (starttime && (starttime>begin) && (starttime<end))
+ {
+ xevent.Clear();
+
+ xmlChar *eventid;
+ eventid=xmlGetProp(node,(const xmlChar *) "eventid");
+ if (eventid)
+ {
+ xevent.SetEventID(atoi((const char *) eventid));
+ xmlFree(eventid);
+ }
+
+ xmlChar *vpsstart=xmlGetProp(node,(const xmlChar *) "vps-start");
+ if (vpsstart)
+ {
+ time_t vps=ConvertXMLTVTime2UnixTime((char *) vpsstart);
+ xevent.SetVps(vps);
+ xmlFree(vpsstart);
+ }
+
+ xevent.SetStartTime(starttime);
+ if (stoptime) xevent.SetDuration(stoptime-starttime);
+ FetchEvent(node);
+
+ cSchedulesLock *schedulesLock = new cSchedulesLock(true,2000); // to be safe ;)
+ const cSchedules *schedules = cSchedules::Schedules(*schedulesLock);
+ if (schedules)
+ {
+ for (int i=0; i<map->NumChannelIDs(); i++)
+ {
+ bool addevents=false;
+ if ((map->Flags() & OPT_APPEND)==OPT_APPEND) addevents=true;
+
+ cChannel *channel=Channels.GetByChannelID(map->ChannelIDs()[i]);
+ cSchedule* schedule = (cSchedule *) schedules->GetSchedule(channel,addevents);
+ if (schedule)
+ {
+ cEvent *event=NULL;
+ if ((event=SearchEvent(schedule,&xevent)))
+ {
+ PutEvent(schedule,event,&xevent,map);
+ }
+ else
+ {
+ if (addevents) PutEvent(schedule,event,&xevent,map);
+ }
+ }
+ }
+ }
+ delete schedulesLock;
+ }
+ xmlFree(channelid);
+ }
+ }
+ }
+ node=node->next;
+ }
+ xmlFreeDoc(xmltv);
+ return true;
+}
+
+cParse::cParse(const char *Name, cEPGMappings *Maps, cTEXTMappings *Texts)
+{
+#if VDRVERSNUM < 10701 || defined(__FreeBSD__)
+ conv = new cCharSetConv("UTF-8",cCharSetConv::SystemCharacterTable() ?
+ cCharSetConv::SystemCharacterTable() : "UTF-8");
+#else
+ conv = new cCharSetConv("UTF-8",NULL);
+#endif
+ name=strdup(Name);
+ maps=Maps;
+ texts=Texts;
+}
+
+cParse::~cParse()
+{
+ delete conv;
+ free(name);
+}
diff --git a/parse.h b/parse.h
new file mode 100644
index 0000000..b613880
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,146 @@
+/*
+ * parse.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _PARSE_H
+#define _PARSE_H
+
+#include <vdr/tools.h>
+#include <vdr/epg.h>
+#include <libxml/parser.h>
+#include <time.h>
+
+#include "maps.h"
+
+class cXMLTVEvent
+{
+private:
+ char *title;
+ char *shorttext;
+ char *description;
+ char *country;
+ char *review;
+ char *system;
+ char *rating;
+ char *origtitle;
+ int year;
+ time_t starttime;
+ int duration;
+ time_t vps;
+ tEventID eventid;
+#if VDRVERSNUM >= 10711
+ uchar parentalRating;
+ uchar contents[MaxEventContents];
+#endif
+public:
+ cXMLTVEvent();
+ ~cXMLTVEvent();
+ void Clear();
+ void SetTitle(const char *Title);
+ void SetOrigTitle(const char *OrigTitle);
+ void SetShortText(const char *ShortText);
+ void SetDescription(const char *Description);
+ bool AddDescription(const char *Name, const char *Value);
+ bool AddDescription(const char *Name, int Value);
+ void SetCountry(const char *Country);
+ void SetReview(const char *Review);
+ void SetRating(const char *System, const char *Rating);
+ 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()
+ {
+ return duration;
+ }
+ time_t StartTime()
+ {
+ 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()
+ {
+ 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;
+ }
+};
+
+class cParse
+{
+private:
+ char *name;
+ cEPGMappings *maps;
+ cTEXTMappings *texts;
+ cXMLTVEvent xevent;
+ cCharSetConv *conv;
+ 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);
+ cEPGMapping *EPGMapping(const char *ChannelName);
+ cTEXTMapping *TEXTMapping(const char *Name);
+ bool PutEvent(cSchedule* schedule,cEvent *event,cXMLTVEvent *xevent, cEPGMapping *map);
+public:
+ cParse(const char *Name, cEPGMappings *Maps, cTEXTMappings *Texts);
+ ~cParse();
+ bool Process(char *buffer, int bufsize);
+};
+
+#endif
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000..87313cf
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,164 @@
+# This file is distributed under the same license as the xmltv2vdr package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2010-12-28 18:28+0100\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"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "options"
+msgstr "Optionen"
+
+msgid "automatic wakeup"
+msgstr "automatisch Aufwachen"
+
+msgid "execution time"
+msgstr "Ausführung um"
+
+msgid "text mapping"
+msgstr "Textzuordnungen"
+
+msgid "epg sources"
+msgstr "EPG Quellen"
+
+msgid "no epgsources installed"
+msgstr "Keine EPG Quellen installiert"
+
+msgid "epg source channels"
+msgstr "EPG Quellkanäle"
+
+msgid "mapped"
+msgstr "zugewiesen"
+
+msgid "up"
+msgstr "hoch"
+
+msgid "down"
+msgstr "runter"
+
+msgid "edit"
+msgstr "editieren"
+
+msgid "texts"
+msgstr "Texte"
+
+msgid "country"
+msgstr "Land"
+
+msgid "year"
+msgstr "Jahr"
+
+msgid "originaltitle"
+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 "country and date"
+msgstr "Ort und Jahr"
+
+msgid "original title"
+msgstr "Originaltitel"
+
+msgid "credits"
+msgstr "Mitwirkende"
+
+msgid "days in advance"
+msgstr "Anzahl Tage"
+
+msgid "channels provided"
+msgstr "Verfügbare Kanäle"
+
+msgid "selected"
+msgstr "ausgewählt"
+
+msgid "epg source channel options"
+msgstr "EPG Quellkanal Optionen"
+
+msgid "type of processing"
+msgstr "Art der Bearbeitung"
+
+msgid "merge"
+msgstr "mischen"
+
+msgid "append"
+msgstr "hinzufügen"
+
+msgid "short text"
+msgstr "Kurztext"
+
+msgid "long text"
+msgstr "Langtext"
+
+msgid "merge long texts"
+msgstr "Langtexte zusammenführen"
+
+msgid "category"
+msgstr "Kategorie"
+
+msgid "rating"
+msgstr "Freigabe"
+
+msgid "video"
+msgstr "Video"
+
+msgid "audio"
+msgstr "Audio"
+
+msgid "vps"
+msgstr "VPS"
+
+msgid "epg source channel mappings"
+msgstr "EPG Quellkanalzuordnungen"
+
+msgid "none"
+msgstr "keine"
+
+msgid "unmap"
+msgstr "entfernen"
+
+msgid "map"
+msgstr "hinzufügen"
+
+msgid "choose"
+msgstr "Auswahl"
+
+msgid "xmltv2vdr plugin still working"
+msgstr "xmltv2vdr plugin ist noch aktiv"
+
diff --git a/setup.cpp b/setup.cpp
new file mode 100644
index 0000000..a5e2aa3
--- /dev/null
+++ b/setup.cpp
@@ -0,0 +1,1034 @@
+/*
+ * setup.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include "setup.h"
+
+#include <vdr/osdbase.h>
+
+#define CHNUMWIDTH (numdigits(Channels.MaxNumber())+1)
+
+char *strcatrealloc(char *dest, const char *src)
+{
+ if (!src || !*src)
+ return dest;
+
+ size_t l = (dest ? strlen(dest) : 0) + strlen(src) + 1;
+ if (dest)
+ {
+ dest = (char *)realloc(dest, l);
+ strcat(dest, src);
+ }
+ else
+ {
+ dest = (char*)malloc(l);
+ strcpy(dest, src);
+ }
+ return dest;
+}
+
+cMenuSetupXmltv2vdr::cMenuSetupXmltv2vdr(cPluginXmltv2vdr *Plugin)
+{
+ baseplugin=Plugin;
+ sourcesBegin=sourcesEnd=mappingBegin=mappingEnd=mappingEntry=0;
+ wakeup=baseplugin->wakeup;
+ exectime=baseplugin->ExecTime();
+ Output();
+}
+
+void cMenuSetupXmltv2vdr::Output(void)
+{
+ if (!baseplugin) return;
+ int current=Current();
+ Clear();
+ cOsdItem *first=newtitle(tr("options"));
+ Add(first,true);
+
+ Add(new cMenuEditBoolItem(tr("automatic wakeup"),&wakeup),true);
+ Add(new cMenuEditTimeItem(tr("execution time"),&exectime),true);
+ Add(new cOsdItem(tr("text mapping")),true);
+ mappingEntry=Current();
+ Add(newtitle(tr("epg sources")),true);
+
+ if (!baseplugin->EPGSourceCount())
+ {
+ baseplugin->ReadInEPGSources();
+ if (!baseplugin->EPGSourceCount())
+ {
+ Add(new cOsdItem(tr("no epgsources installed"),osUnknown,false));
+ Display();
+ return;
+ }
+ }
+ sourcesBegin=Current()+1;
+ for (int i=0; i<baseplugin->EPGSourceCount(); i++)
+ {
+ cEPGSource *epgsrc=baseplugin->EPGSource(i);
+ if (epgsrc)
+ {
+ Add(new cOsdItem(epgsrc->Name()),true);
+ }
+ }
+ sourcesEnd=Current();
+
+ Add(newtitle(tr("epg source channels")),true);
+ generatesumchannellist();
+ mappingBegin=Current()+1;
+ for (int i=0; i<channels.Size(); i++)
+ {
+ bool mapped=false;
+ cEPGMapping *map=baseplugin->EPGMapping(channels[i]);
+ if (map)
+ {
+ if (map->NumChannelIDs()) mapped=true;
+ }
+ cString buffer = cString::sprintf ("%s:\t%s",channels[i],mapped ? tr("mapped") : "");
+ Add (new cOsdItem (buffer),true);
+ }
+ mappingEnd=Current();
+
+ if (current==-1)
+ {
+ SetCurrent(first);
+ CursorDown();
+ }
+ else
+ {
+ SetCurrent(Get(current));
+ }
+ Display();
+}
+
+void cMenuSetupXmltv2vdr::generatesumchannellist()
+{
+ cStringList oldchannels;
+ for (int i=0; i<channels.Size(); i++)
+ {
+ oldchannels.Append(strdup(channels[i]));
+ }
+
+ channels.Clear();
+ 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 (channellist->Get(x)->InUse())
+ {
+ bool found=false;
+ for (int t=0; t<channels.Size(); t++)
+ {
+ if (!strcmp(channels[t],channellist->Get(x)->Name())) found=true;
+ }
+ if (!found)
+ {
+ channels.Append(strdup(channellist->Get(x)->Name()));
+ }
+
+ }
+ }
+ }
+ }
+ }
+ channels.Sort();
+
+ for (int i=0; i<oldchannels.Size(); i++)
+ {
+ if (channels.Find(oldchannels[i])==-1)
+ {
+ cEPGMapping *map=baseplugin->EPGMapping(oldchannels[i]);
+ if (map)
+ {
+ map->ReplaceChannels(0,NULL);
+ map->ChangeFlags(0);
+ char *name=NULL;
+ if (asprintf(&name,"channel.%s",map->ChannelName())!=-1)
+ {
+ SetupStore(name,"1;0;");
+ free(name);
+ }
+ }
+ }
+ }
+}
+
+cOsdItem *cMenuSetupXmltv2vdr::newtitle(const char *s)
+{
+ cString buffer = cString::sprintf("---- %s ----", s);
+ return new cOsdItem (buffer,osUnknown,false);
+}
+
+void cMenuSetupXmltv2vdr::Store(void)
+{
+ SetupStore("options.exectime",exectime);
+ SetupStore("options.wakeup",wakeup);
+
+ baseplugin->wakeup=wakeup;
+ baseplugin->SetExecTime(exectime);
+}
+
+eOSState cMenuSetupXmltv2vdr::edit()
+{
+ if ((Current()>=sourcesBegin) && (Current()<=sourcesEnd))
+ {
+ return AddSubMenu(new cMenuSetupXmltv2vdrChannelSource(baseplugin,this,Current()-sourcesBegin));
+ }
+
+ if ((Current()>=mappingBegin) && (Current()<=mappingEnd))
+ {
+ return AddSubMenu(new cMenuSetupXmltv2vdrChannelMap(baseplugin,this,Current()-mappingBegin));
+ }
+ if (Current()==mappingEntry)
+ {
+ return AddSubMenu(new cMenuSetupXmltv2vdrTextMap(baseplugin));
+ }
+ return osUnknown;
+}
+
+eOSState cMenuSetupXmltv2vdr::ProcessKey (eKeys Key)
+{
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ if (HasSubMenu()) return osContinue;
+ switch (state)
+ {
+ case osContinue:
+ if ((Key==kDown) || (Key==kUp))
+ {
+ if ((Current()>=sourcesBegin) && (Current()<=sourcesEnd))
+ {
+ if ((sourcesEnd-sourcesBegin)>0)
+ {
+ SetHelp(NULL,tr("up"),tr("down"),tr("edit"));
+ }
+ else
+ {
+ SetHelp(NULL,NULL,NULL,tr("edit"));
+ }
+ }
+ else if (((Current()>=mappingBegin) && (Current()<=mappingEnd)) || (Current()==mappingEntry))
+ {
+ SetHelp(NULL,NULL,NULL,tr("edit"));
+ }
+ else
+ {
+ SetHelp(NULL,NULL,NULL,NULL);
+ }
+ }
+ break;
+
+ case osUnknown:
+ if ((Key==kOk) || (Key==kBlue))
+ {
+ state=edit();
+ if (state==osUnknown)
+ {
+ Store();
+ state=osBack;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return state;
+}
+
+// --------------------------------------------------------------------------------------------------------
+
+cMenuSetupXmltv2vdrTextMap::cMenuSetupXmltv2vdrTextMap(cPluginXmltv2vdr *Plugin)
+{
+ baseplugin=Plugin;
+ SetSection(cString::sprintf("%s '%s' : %s",trVDR("Plugin"), baseplugin->Name(), tr("texts")));
+ SetPlugin(baseplugin);
+
+ cTEXTMapping *textmap;
+
+ textmap=baseplugin->TEXTMapping("country");
+ if (textmap)
+ {
+ strn0cpy(country,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(country,tr("country"));
+ }
+
+ textmap=baseplugin->TEXTMapping("date");
+ if (textmap)
+ {
+ strn0cpy(date,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(date,tr("year"));
+ }
+
+ textmap=baseplugin->TEXTMapping("originaltitle");
+ if (textmap)
+ {
+ strn0cpy(originaltitle,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(originaltitle,tr("originaltitle"));
+ }
+
+ textmap=baseplugin->TEXTMapping("director");
+ if (textmap)
+ {
+ strn0cpy(director,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(director,tr("director"));
+ }
+
+ textmap=baseplugin->TEXTMapping("actor");
+ if (textmap)
+ {
+ strn0cpy(actor,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(actor,tr("actor"));
+ }
+
+ textmap=baseplugin->TEXTMapping("writer");
+ if (textmap)
+ {
+ strn0cpy(writer,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(writer,tr("writer"));
+ }
+
+ textmap=baseplugin->TEXTMapping("adapter");
+ if (textmap)
+ {
+ strn0cpy(adapter,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(adapter,tr("adapter"));
+ }
+
+ textmap=baseplugin->TEXTMapping("producer");
+ if (textmap)
+ {
+ strn0cpy(producer,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(producer,tr("producer"));
+ }
+
+ textmap=baseplugin->TEXTMapping("composer");
+ if (textmap)
+ {
+ strn0cpy(composer,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(composer,tr("composer"));
+ }
+
+ textmap=baseplugin->TEXTMapping("editor");
+ if (textmap)
+ {
+ strn0cpy(editor,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(editor,tr("editor"));
+ }
+
+ textmap=baseplugin->TEXTMapping("presenter");
+ if (textmap)
+ {
+ strn0cpy(presenter,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(presenter,tr("presenter"));
+ }
+
+ textmap=baseplugin->TEXTMapping("commentator");
+ if (textmap)
+ {
+ strn0cpy(commentator,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(commentator,tr("commentator"));
+ }
+
+ textmap=baseplugin->TEXTMapping("guest");
+ if (textmap)
+ {
+ strn0cpy(guest,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(guest,tr("guest"));
+ }
+
+ textmap=baseplugin->TEXTMapping("review");
+ if (textmap)
+ {
+ strn0cpy(review,textmap->Value(),strlen(textmap->Value())+1);
+ }
+ else
+ {
+ strcpy(review,tr("review"));
+ }
+
+
+ Add(newtitle(tr("country and date")));
+ Add(new cMenuEditStrItem("country",country,sizeof(country)));
+ Add(new cMenuEditStrItem("date",date,sizeof(date)));
+
+ Add(newtitle(tr("original title")));
+ Add(new cMenuEditStrItem("originaltitle",originaltitle,sizeof(originaltitle)));
+
+ Add(newtitle(tr("credits")));
+ Add(new cMenuEditStrItem("actor",actor,sizeof(actor)));
+ Add(new cMenuEditStrItem("guest",guest,sizeof(guest)));
+ Add(new cMenuEditStrItem("director",director,sizeof(director)));
+ Add(new cMenuEditStrItem("writer",writer,sizeof(writer)));
+ Add(new cMenuEditStrItem("composer",composer,sizeof(composer)));
+ Add(new cMenuEditStrItem("editor",editor,sizeof(editor)));
+ Add(new cMenuEditStrItem("producer",producer,sizeof(producer)));
+ Add(new cMenuEditStrItem("adapter",adapter,sizeof(adapter)));
+ Add(new cMenuEditStrItem("commentator",commentator,sizeof(commentator)));
+ Add(new cMenuEditStrItem("presenter",presenter,sizeof(presenter)));
+
+ Add(newtitle(tr("review")));
+ Add(new cMenuEditStrItem("review",review,sizeof(review)));
+}
+
+cOsdItem *cMenuSetupXmltv2vdrTextMap::newtitle(const char *s)
+{
+ cString buffer = cString::sprintf("---- %s ----", s);
+ return new cOsdItem (buffer,osUnknown,false);
+}
+
+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("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));
+ }
+
+ SetupStore("textmap.country",country);
+ SetupStore("textmap.date",date);
+ SetupStore("textmap.originaltitle",originaltitle);
+ SetupStore("textmap.actor",actor);
+ SetupStore("textmap.adapter",adapter);
+ SetupStore("textmap.commentator",commentator);
+ SetupStore("textmap.composer",composer);
+ SetupStore("textmap.director",director);
+ SetupStore("textmap.editor",editor);
+ SetupStore("textmap.guest",guest);
+ SetupStore("textmap.presenter",presenter);
+ SetupStore("textmap.producer",producer);
+ SetupStore("textmap.writer",writer);
+ SetupStore("textmap.review",review);
+}
+
+// --------------------------------------------------------------------------------------------------------
+
+cMenuSetupXmltv2vdrChannelSource::cMenuSetupXmltv2vdrChannelSource(cPluginXmltv2vdr *Plugin, cMenuSetupXmltv2vdr *Menu, int Index)
+{
+ menu=Menu;
+ baseplugin=Plugin;
+ sel=NULL;
+ days=0;
+
+ epgsrc=baseplugin->EPGSource(Index);
+ if (!epgsrc) return;
+
+ SetSection(cString::sprintf("%s '%s' : %s",trVDR("Plugin"), baseplugin->Name(), epgsrc->Name()));
+
+ Add(newtitle(tr("options")));
+ days=epgsrc->DaysInAdvance();
+ Add(new cMenuEditIntItem(tr("days in advance"),&days,1,epgsrc->DaysMax()));
+
+ Add(newtitle(tr("channels provided")));
+
+ cEPGChannels *channellist=epgsrc->ChannelList();
+ if (!channellist) return;
+
+ sel=new int[channellist->Count()];
+ if (!sel) return;
+
+ for (int i=0; i<channellist->Count(); i++)
+ {
+ if (channellist->Get(i)->InUse())
+ {
+ sel[i]=1;
+ }
+ else
+ {
+ sel[i]=0;
+ }
+ Add(new cMenuEditBoolItem(channellist->Get(i)->Name(),&sel[i],"",tr("selected")));
+ }
+}
+
+cMenuSetupXmltv2vdrChannelSource::~cMenuSetupXmltv2vdrChannelSource()
+{
+ if (sel) delete [] sel;
+ if (menu) menu->Output();
+}
+
+cOsdItem *cMenuSetupXmltv2vdrChannelSource::newtitle(const char *s)
+{
+ cString buffer = cString::sprintf("---- %s ----", s);
+ return new cOsdItem (buffer,osUnknown,false);
+}
+
+void cMenuSetupXmltv2vdrChannelSource::Store(void)
+{
+ if ((!baseplugin) || (!sel) || (!epgsrc)) return;
+
+ epgsrc->ChangeChannelSelection(sel);
+ epgsrc->ChangeDaysInAdvance(days);
+ 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);
+ }
+ }
+ }
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------------------
+
+cMenuSetupXmltv2vdrChannelMap::cMenuSetupXmltv2vdrChannelMap(cPluginXmltv2vdr *Plugin, cMenuSetupXmltv2vdr *Menu, int Index)
+{
+ baseplugin=Plugin;
+ menu=Menu;
+ hasmaps=false;
+ flags=0;
+ days=1;
+ if (Index>menu->ChannelList()->Size()) return;
+ channel=(*menu->ChannelList())[Index];
+ if (!channel) return;
+
+ SetPlugin(baseplugin);
+
+ cEPGMapping *oldmap=baseplugin->EPGMapping(channel);
+ if (oldmap)
+ {
+ map=new cEPGMapping(*oldmap);
+ }
+ else
+ {
+ map=new cEPGMapping(channel,NULL);
+ }
+ if (!map) return;
+
+ title=cString::sprintf("%s - %s '%s' : %s",trVDR("Setup"),trVDR("Plugin"), baseplugin->Name(), channel);
+ SetTitle(title);
+
+ flags=map->Flags();
+ days=map->Days();
+ daysmax=getdaysmax();
+ output();
+}
+
+cMenuSetupXmltv2vdrChannelMap::~cMenuSetupXmltv2vdrChannelMap()
+{
+ if (map) delete map;
+ if (menu) menu->Output();
+}
+
+int cMenuSetupXmltv2vdrChannelMap::getdaysmax()
+{
+ if (!baseplugin) return 1;
+
+ int ret=INT_MAX;
+ 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 (channellist->Get(x)->InUse())
+ {
+ if (epgsrc->DaysMax()<ret) ret=epgsrc->DaysMax();
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (ret==INT_MAX) ret=1;
+ return ret;
+}
+
+cOsdItem *cMenuSetupXmltv2vdrChannelMap::option(const char *s, bool yesno)
+{
+ cString buffer = cString::sprintf("%s:\t%s", s, yesno ? trVDR("yes") : trVDR("no"));
+ return new cOsdItem (buffer,osUnknown,false);
+}
+
+cOsdItem *cMenuSetupXmltv2vdrChannelMap::newtitle(const char *s)
+{
+ cString buffer = cString::sprintf("---- %s ----", s);
+ return new cOsdItem (buffer,osUnknown,false);
+}
+
+void cMenuSetupXmltv2vdrChannelMap::output(void)
+{
+ if (!map) return;
+
+ int current=Current();
+
+ Clear();
+
+ Add(newtitle(tr("epg source channel options")));
+
+ Add(new cMenuEditIntItem(tr("days in advance"),&days,1,daysmax));
+ Add(new cMenuEditBitItem(tr("type of processing"),&flags,OPT_APPEND,tr("merge"),tr("append")));
+
+ if ((flags & OPT_APPEND)!=OPT_APPEND)
+ {
+ Add(new cMenuEditBitItem(tr("short text"),&flags,USE_SHORTTEXT));
+ Add(new cMenuEditBitItem(tr("long text"),&flags,USE_LONGTEXT));
+ if ((flags & USE_LONGTEXT)==USE_LONGTEXT)
+ {
+ Add(new cMenuEditBitItem(tr("merge long texts"),&flags,OPT_MERGELTEXT));
+ }
+ else
+ {
+ Add(option(tr("merge long texts"),false));
+ }
+ }
+ else
+ {
+ Add(option(tr("short text"),true));
+ Add(option(tr("long text"),true));
+ Add(option(tr("merge long texts"),false));
+ }
+ Add(new cMenuEditBitItem(tr("country and date"),&flags,USE_COUNTRYDATE));
+ Add(new cMenuEditBitItem(tr("original title"),&flags,USE_ORIGTITLE));
+ Add(new cMenuEditBitItem(tr("category"),&flags,USE_CATEGORY));
+ Add(new cMenuEditBitItem(tr("credits"),&flags,USE_CREDITS));
+ Add(new cMenuEditBitItem(tr("rating"),&flags,USE_RATING));
+ Add(new cMenuEditBitItem(tr("review"),&flags,USE_REVIEW));
+ Add(new cMenuEditBitItem(tr("video"),&flags,USE_VIDEO));
+ Add(new cMenuEditBitItem(tr("audio"),&flags,USE_AUDIO));
+ Add(new cMenuEditBitItem(tr("vps"),&flags,OPT_VPS));
+
+ hasmaps=false;
+ Add(newtitle(tr("epg source channel mappings")));
+ for (int i=0; i<map->NumChannelIDs(); i++)
+ {
+ cChannel *chan=Channels.GetByChannelID(map->ChannelIDs()[i]);
+ if (chan)
+ {
+ cString buffer = cString::sprintf("%-*i %s", CHNUMWIDTH, chan->Number(),chan->Name());
+ Add(new cOsdItem(buffer));
+ hasmaps=true;
+ }
+ }
+ if (!hasmaps) Add(new cOsdItem(tr("none")));
+ if (current!=-1) for (int i=1; i<=current; i++) CursorDown();
+
+ Display();
+}
+
+eOSState cMenuSetupXmltv2vdrChannelMap::ProcessKey (eKeys Key)
+{
+ cOsdItem *item=NULL;
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ if (HasSubMenu()) return osContinue;
+ if (state==osContinue)
+ {
+ switch (Key)
+ {
+ case kLeft:
+ case kRight:
+ if ((Current()==2) || (Current()==4)) output();
+ break;
+ case kDown:
+ if (Current()>=16)
+ SetHelp(tr("unmap"),tr("map"));
+ break;
+ case kUp:
+ if (Current()<16)
+ SetHelp(NULL,NULL);
+ default:
+ break;
+ }
+ }
+
+ if (state==osUnknown)
+ {
+ switch (Key)
+ {
+ case kOk:
+ if ((Current()>=16) && (!hasmaps))
+ {
+ return AddSubMenu(new cMenuSetupXmltv2vdrChannelsVDR(baseplugin,this,channel,title));
+ }
+ else
+ {
+ Store();
+ state=osBack;
+ }
+ break;
+ case kRed:
+ item=Get(Current());
+ if (item)
+ {
+ if (map)
+ {
+ map->RemoveChannel(atoi(item->Text()));
+ output();
+ }
+ }
+ break;
+ case kGreen:
+ return AddSubMenu(new cMenuSetupXmltv2vdrChannelsVDR(baseplugin,this,channel,title));
+ break;
+ default:
+ break;
+ }
+ }
+ return state;
+}
+
+void cMenuSetupXmltv2vdrChannelMap::AddChannel2Map(int ChannelNumber)
+{
+ if (map)
+ {
+ map->AddChannel(ChannelNumber);
+ output();
+ }
+}
+
+bool cMenuSetupXmltv2vdrChannelMap::EPGMappingExists(tChannelID ChannelID)
+{
+ if (!map) return true;
+ for (int i=0; i<map->NumChannelIDs(); i++)
+ {
+ if (map->ChannelIDs()[i]==ChannelID) return true;
+ }
+ return false;
+}
+
+void cMenuSetupXmltv2vdrChannelMap::epgmappingreplace(cEPGMapping *newmapping)
+{
+ if (!newmapping) return;
+ cEPGMapping *map=baseplugin->EPGMapping(newmapping->ChannelName());
+ if (!map)
+ {
+ map=new cEPGMapping(*newmapping);
+ baseplugin->EPGMappingAdd(map);
+ }
+ else
+ {
+ map->ChangeFlags(newmapping->Flags());
+ map->ChangeDays(newmapping->Days());
+ map->ReplaceChannels(newmapping->NumChannelIDs(),newmapping->ChannelIDs());
+ }
+}
+
+void cMenuSetupXmltv2vdrChannelMap::Store(void)
+{
+ if (!channel) return;
+ char *name=NULL;
+ if (asprintf(&name,"channel.%s",channel)==-1) return;
+
+ char *value=NULL;
+ if (asprintf(&value,"%i;%i;",days,flags)==-1)
+ {
+ free(name);
+ return;
+ }
+ for (int i=0; i<map->NumChannelIDs(); i++)
+ {
+ cString ChannelID = map->ChannelIDs()[i].ToString();
+ value=strcatrealloc(value,*ChannelID);
+ if (i<map->NumChannelIDs()-1) value=strcatrealloc(value,";");
+ }
+
+ 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;
+ }
+ }
+ }
+ }
+ }
+
+}
+
+// --------------------------------------------------------------------------------------------------------
+
+cMenuSetupXmltv2vdrChannelsVDR::cMenuSetupXmltv2vdrChannelsVDR(cPluginXmltv2vdr *Plugin,
+ cMenuSetupXmltv2vdrChannelMap *Map, const char *Channel, cString Title)
+ :cOsdMenu("",CHNUMWIDTH)
+{
+ baseplugin=Plugin;
+ map=Map;
+ SetHelp(NULL,NULL,tr("choose"));
+ SetTitle(Title);
+
+ for (cChannel *channel = Channels.First(); channel; channel=Channels.Next(channel))
+ {
+ if (!channel->GroupSep())
+ {
+ cString buf= cString::sprintf("%d\t%s",channel->Number(),channel->Name());
+ if ((epgmappingexists(channel->GetChannelID(),Channel)) || (map->EPGMappingExists(channel->GetChannelID())))
+ {
+ Add(new cOsdItem(buf,osUnknown,false));
+ }
+ else
+ {
+ Add(new cOsdItem(buf));
+ }
+ }
+ }
+}
+
+bool cMenuSetupXmltv2vdrChannelsVDR::epgmappingexists(tChannelID channelid, const char *channel2ignore)
+{
+ if (!baseplugin) return true;
+ if (!baseplugin->EPGMappingCount()) return false;
+ for (int i=0; i<baseplugin->EPGMappingCount(); i++)
+ {
+ if (channel2ignore && !strcmp(baseplugin->EPGMapping(i)->ChannelName(),channel2ignore)) continue;
+ for (int x=0; x<baseplugin->EPGMapping(i)->NumChannelIDs(); x++)
+ {
+ if (baseplugin->EPGMapping(i)->ChannelIDs()[x]==channelid) return true;
+ }
+ }
+ return false;
+}
+
+eOSState cMenuSetupXmltv2vdrChannelsVDR::ProcessKey (eKeys Key)
+{
+ cOsdItem *item=NULL;
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ if (HasSubMenu()) return osContinue;
+ if (state==osUnknown)
+ {
+ switch (Key)
+ {
+ case kBack:
+ state=osBack;
+ break;
+ case kYellow:
+ case kOk:
+ item=Get(Current());
+ if (item)
+ {
+ if (map) map->AddChannel2Map(atoi(item->Text()));
+ }
+ state=osBack;
+ break;
+ default:
+ break;
+ }
+ }
+ return state;
+}
diff --git a/setup.h b/setup.h
new file mode 100644
index 0000000..96222d1
--- /dev/null
+++ b/setup.h
@@ -0,0 +1,119 @@
+/*
+ * setup.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef __setup_h
+#define __setup_h
+
+#include <vdr/menuitems.h>
+#include "xmltv2vdr.h"
+
+class cMenuSetupXmltv2vdr : public cMenuSetupPage
+{
+protected:
+ virtual void Store(void);
+private:
+ cStringList channels;
+ cPluginXmltv2vdr *baseplugin;
+ int mappingBegin,mappingEnd;
+ int sourcesBegin,sourcesEnd;
+ int mappingEntry;
+ cOsdItem *newtitle (const char *s);
+ eOSState edit(void);
+ void generatesumchannellist();
+ int exectime;
+ int wakeup;
+public:
+ void Output(void);
+ cMenuSetupXmltv2vdr(cPluginXmltv2vdr *Plugin);
+ virtual eOSState ProcessKey(eKeys Key);
+ cStringList *ChannelList()
+ {
+ return &channels;
+ }
+};
+
+class cMenuSetupXmltv2vdrTextMap : public cMenuSetupPage
+{
+protected:
+ virtual void Store(void);
+private:
+ cPluginXmltv2vdr *baseplugin;
+ char country[255];
+ char date[255];
+ char originaltitle[255];
+ char director[255];
+ char actor[255];
+ char writer[255];
+ char adapter[255];
+ char producer[255];
+ char composer[255];
+ char editor[255];
+ char presenter[255];
+ char commentator[255];
+ char guest[255];
+ char review[255];
+ cOsdItem *newtitle (const char *s);
+public:
+ cMenuSetupXmltv2vdrTextMap(cPluginXmltv2vdr *Plugin);
+};
+
+class cMenuSetupXmltv2vdrChannelSource : public cMenuSetupPage
+{
+protected:
+ virtual void Store(void);
+private:
+ cMenuSetupXmltv2vdr *menu;
+ cPluginXmltv2vdr *baseplugin;
+ cEPGSource *epgsrc;
+ int *sel;
+ int days;
+ cOsdItem *newtitle (const char *s);
+public:
+ cMenuSetupXmltv2vdrChannelSource(cPluginXmltv2vdr *Plugin, cMenuSetupXmltv2vdr *Menu, int Index);
+ ~cMenuSetupXmltv2vdrChannelSource();
+};
+
+class cMenuSetupXmltv2vdrChannelMap : public cMenuSetupPage
+{
+protected:
+ virtual void Store(void);
+private:
+ cPluginXmltv2vdr *baseplugin;
+ cMenuSetupXmltv2vdr *menu;
+ cEPGMapping *map;
+ bool hasmaps;
+ uint flags;
+ int days;
+ int daysmax;
+ void output(void);
+ cString title;
+ const char *channel;
+ int getdaysmax();
+ cOsdItem *newtitle (const char *s);
+ cOsdItem *option(const char *s, bool yesno);
+ void epgmappingreplace(cEPGMapping *newmapping);
+public:
+ cMenuSetupXmltv2vdrChannelMap(cPluginXmltv2vdr *Plugin, cMenuSetupXmltv2vdr *Menu, int Index);
+ ~cMenuSetupXmltv2vdrChannelMap();
+ void AddChannel2Map(int ChannelNumber);
+ bool EPGMappingExists(tChannelID ChannelID);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+class cMenuSetupXmltv2vdrChannelsVDR : public cOsdMenu
+{
+private:
+ cPluginXmltv2vdr *baseplugin;
+ cMenuSetupXmltv2vdrChannelMap *map;
+ bool epgmappingexists(tChannelID channelid, const char *channel2ignore);
+public:
+ cMenuSetupXmltv2vdrChannelsVDR(cPluginXmltv2vdr *Plugin, cMenuSetupXmltv2vdrChannelMap *Map,
+ const char *Channel, cString Title);
+ virtual eOSState ProcessKey(eKeys Key);
+};
+
+#endif
diff --git a/xmltv2vdr.cpp b/xmltv2vdr.cpp
new file mode 100644
index 0000000..1b2f589
--- /dev/null
+++ b/xmltv2vdr.cpp
@@ -0,0 +1,641 @@
+/*
+ * xmltv2vdr.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#include <vdr/plugin.h>
+#include <string.h>
+#include <sys/wait.h>
+#include "xmltv2vdr.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);
+}
+
+int cEPGChannel::Compare(const cListObject &ListObject) const
+{
+ cEPGChannel *epgchannel= (cEPGChannel *) &ListObject;
+ return strcmp(name,epgchannel->Name());
+}
+
+// -------------------------------------------------------------
+
+cEPGExecute::cEPGExecute(const char *Name, cEPGMappings *Maps, cTEXTMappings *Texts):cThread(Name)
+{
+ dsyslog("xmltv2vdr: added epgsource '%s'",Name);
+ name=strdup(Name);
+ pipe=false;
+ daysinadvance=0;
+ ready2parse=ReadConfig();
+ parse=new cParse(Name, Maps, Texts);
+}
+
+cEPGExecute::~cEPGExecute()
+{
+ Stop();
+ free((void*) name);
+ if (parse) delete parse;
+}
+
+bool cEPGExecute::ReadConfig()
+{
+ dsyslog("xmltv2vdr: reading config of epgsource '%s'",name);
+ char *fname=NULL;
+ if (asprintf(&fname,"%s/%s",EPGSOURCES,name)!=-1)
+ {
+ FILE *f=fopen(fname,"r+");
+ if (f)
+ {
+ size_t lsize;
+ char *line=NULL;
+ int linenr=1;
+ while (getline(&line,&lsize,f)!=-1)
+ {
+ if (linenr==1)
+ {
+ if (!strncmp(line,"pipe",4))
+ {
+ dsyslog("xmltv2vdr: '%s' is providing data through a pipe",name);
+ pipe=true;
+ }
+ else
+ {
+ dsyslog("xmltv2vdr: '%s' is providing data through a file",name);
+ pipe=false;
+ }
+ }
+ if (linenr==2)
+ {
+ char *semicolon=strchr(line,';');
+ if (semicolon)
+ {
+ *semicolon=0;
+ semicolon++;
+ daysinadvance=atoi(line);
+ daysmax=atoi(semicolon);
+ dsyslog("xmltv2vdr: '%s' days=%i/%i",name,daysinadvance,daysmax);
+ }
+ }
+ if (linenr>2)
+ {
+ // channels
+ char *semicolon=strchr(line,';');
+ if (semicolon) *semicolon=0;
+ bool used=false;
+ char *cname=line;
+ if (line[0]=='*')
+ {
+ cname++;
+ used=true;
+ }
+ cEPGChannel *epgchannel= new cEPGChannel(cname,used);
+ if (epgchannel) channels.Add(epgchannel);
+ }
+ linenr++;
+ }
+ if (line) free(line);
+ channels.Sort();
+ fclose(f);
+ free(fname);
+ return true;
+ }
+ else
+ {
+ esyslog("xmltv2vdr: '%s' cannot read config file",name);
+ }
+ free(fname);
+ }
+ return false;
+}
+
+void cEPGExecute::SetChannelSelection(int *Selection)
+{
+ for (int i=0; i<channels.Count(); i++)
+ {
+ channels.Get(i)->SetUsage(Selection[i]);
+ }
+}
+
+void cEPGExecute::Action()
+{
+ if (!ready2parse) return;
+ if (!parse) return;
+ char *result=NULL;
+ int l=0;
+
+ if (pipe)
+ {
+ cExtPipe p;
+ if (p.Open(name,"r"))
+ {
+ dsyslog("xmltv2vdr: executing epgsource '%s'",name);
+ int c;
+ while ((c=fgetc(p))!=EOF)
+ {
+ if (l%20==0) result=(char *) realloc(result, l+21);
+ result[l++]=c;
+ }
+ int status;
+ if (p.Close(status)>0)
+ {
+ int returncode=WEXITSTATUS(status);
+ if ((!returncode) && (result))
+ {
+ dsyslog("xmltv2vdr: parsing output of '%s'",name);
+ result[l]=0;
+ if (!parse->Process(result,l))
+ {
+ esyslog("xmltv2vdr: failed to parse output of '%s'",name);
+ }
+ }
+ else
+ {
+ esyslog("xmltv2vdr: epgsource '%s' returned with %i",name,returncode);
+ }
+ }
+ if (result) free(result);
+ }
+ else
+ {
+ esyslog("xmltv2vdr: failed to open pipe for '%s'",name);
+ }
+ }
+ else
+ {
+ char *fname=NULL;
+ if (asprintf(&fname,"%s/%s.xmltv",EPGSOURCES,name)!=-1)
+ {
+ int fd=open(fname,O_RDONLY);
+ if (fd!=-1)
+ {
+ struct stat statbuf;
+ if (fstat(fd,&statbuf)!=-1)
+ {
+ l=statbuf.st_size;
+ result=(char *) malloc(l+1);
+ if (result)
+ {
+ if (read(fd,result,statbuf.st_size)==statbuf.st_size)
+ {
+ parse->Process(result,l);
+ }
+ free(result);
+ }
+ }
+ close(fd);
+ }
+ else
+ {
+ esyslog("xmltv2vdr: failed to open file '%s' for '%s'",fname,name);
+ }
+ free(fname);
+ }
+ }
+}
+
+// -------------------------------------------------------------
+
+cEPGSource::cEPGSource(const char *Name, cEPGMappings *Maps, cTEXTMappings *Texts):exec(Name,Maps,Texts)
+{
+ name=strdup(Name);
+}
+
+cEPGSource::~cEPGSource()
+{
+ dsyslog("xmltv2vdr: removed epgsource '%s'",name);
+ free((void *) name);
+}
+
+bool cEPGSource::Execute()
+{
+ if (exec.Active()) return false;
+ exec.Start();
+ return true;
+}
+
+void cEPGSource::Store(void)
+{
+ char *fname1=NULL;
+ char *fname2=NULL;
+ if (asprintf(&fname1,"%s/%s",EPGSOURCES,name)==-1) return;
+ if (asprintf(&fname2,"%s/%s.new",EPGSOURCES,name)==-1)
+ {
+ free(fname1);
+ return;
+ }
+
+ FILE *r=fopen(fname1,"r+");
+ if (!r)
+ {
+ free(fname1);
+ free(fname2);
+ return;
+ }
+ int oldmask=umask(0664);
+ FILE *w=fopen(fname2,"w+");
+ umask(oldmask);
+ if (!w)
+ {
+ fclose(w);
+ unlink(fname2);
+ free(fname1);
+ free(fname2);
+ return;
+ }
+
+ char *line=NULL;
+ size_t lsize;
+ int linenr=1;
+ while (getline(&line,&lsize,r)!=-1)
+ {
+ if (linenr==2)
+ {
+ fprintf(w,"%i;%i\n",DaysInAdvance(),DaysMax());
+ }
+ else if (linenr>2)
+ {
+ char *txt=line;
+ if (txt[0]=='*') txt++;
+ for (int i=0; i<ChannelList()->Count(); i++)
+ {
+ if (!strncmp(txt,ChannelList()->Get(i)->Name(),strlen(ChannelList()->Get(i)->Name())))
+ {
+ if (ChannelList()->Get(i)->InUse())
+ {
+ fprintf(w,"*%s",txt);
+ }
+ else
+ {
+ fprintf(w,"%s",txt);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ fprintf(w,"%s",line);
+ }
+ linenr++;
+ }
+ if (line) free(line);
+ struct stat statbuf;
+ if (fstat(fileno(r),&statbuf)!=-1)
+ {
+ if (fchown(fileno(w),statbuf.st_uid,statbuf.st_gid)) {};
+ if (fchmod(fileno(w),statbuf.st_mode | S_IRGRP | S_IWGRP)) {};
+ }
+ fclose(w);
+ fclose(r);
+ rename(fname2,fname1);
+ free(fname1);
+ free(fname2);
+}
+
+// -------------------------------------------------------------
+
+bool cPluginXmltv2vdr::epgsourceexists(const char *name)
+{
+ if (!epgsources.Count()) return false;
+ for (cEPGSource *epgs=epgsources.First(); epgs; epgs=epgsources.Next(epgs))
+ {
+ if (!strcmp(epgs->Name(),name)) return true;
+ }
+ return false;
+}
+
+void cPluginXmltv2vdr::removeepgmappings()
+{
+ cEPGMapping *maps;
+ while ((maps=epgmappings.Last())!=NULL)
+ {
+ epgmappings.Del(maps);
+ }
+}
+
+void cPluginXmltv2vdr::removetextmappings()
+{
+ cTEXTMapping *maps;
+ while ((maps=textmappings.Last())!=NULL)
+ {
+ textmappings.Del(maps);
+ }
+}
+
+void cPluginXmltv2vdr::removeepgsources()
+{
+ cEPGSource *epgs;
+ while ((epgs=epgsources.Last())!=NULL)
+ {
+ epgsources.Del(epgs);
+ }
+}
+
+bool cPluginXmltv2vdr::epgsourcesactive()
+{
+ bool ret=false;
+ for (cEPGSource *epgs=epgsources.First(); epgs; epgs=epgsources.Next(epgs))
+ {
+ ret|=epgs->Active();
+ }
+ return ret;
+}
+
+bool cPluginXmltv2vdr::executeepgsources()
+{
+ bool ret=false;
+ for (cEPGSource *epgs=epgsources.First(); epgs; epgs=epgsources.Next(epgs))
+ {
+ ret|=epgs->Execute();
+ }
+ return ret;
+}
+
+cEPGMapping *cPluginXmltv2vdr::EPGMapping(const char *ChannelName)
+{
+ if (!epgmappings.Count()) return NULL;
+ for (cEPGMapping *maps=epgmappings.First(); maps; maps=epgmappings.Next(maps))
+ {
+ if (!strcmp(maps->ChannelName(),ChannelName)) return maps;
+ }
+ return NULL;
+}
+
+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;
+}
+
+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))
+ {
+ char *path=NULL;
+ if (asprintf(&path,"%s/%s",EPGSOURCES,dirent->d_name)!=-1)
+ {
+ if (access(path,R_OK|W_OK)!=-1)
+ {
+ epgsources.Add(new cEPGSource(dirent->d_name,&epgmappings,&textmappings));
+ }
+ else
+ {
+ esyslog("xmltv2vdr: cannot access config file for '%s'",dirent->d_name);
+ }
+ free(path);
+ }
+ }
+ }
+ closedir(dir);
+}
+
+void cPluginXmltv2vdr::SetExecTime(int ExecTime)
+{
+ exectime=ExecTime;
+ exectime_t=cTimer::SetTime(time(NULL),cTimer::TimeToInt(exectime));
+ last_exectime_t=0;
+}
+
+cPluginXmltv2vdr::cPluginXmltv2vdr(void)
+{
+ // Initialize any member variables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+ wakeup=0;
+ SetExecTime(200);
+ TEXTMappingAdd(new cTEXTMapping("country",tr("country")));
+ TEXTMappingAdd(new cTEXTMapping("date",tr("year")));
+ TEXTMappingAdd(new cTEXTMapping("originaltitle",tr("originaltitle")));
+ TEXTMappingAdd(new cTEXTMapping("actor",tr("actor")));
+ TEXTMappingAdd(new cTEXTMapping("adapter",tr("adapter")));
+ TEXTMappingAdd(new cTEXTMapping("commentator",tr("commentator")));
+ TEXTMappingAdd(new cTEXTMapping("composer",tr("composer")));
+ TEXTMappingAdd(new cTEXTMapping("director",tr("director")));
+ TEXTMappingAdd(new cTEXTMapping("editor",tr("editor")));
+ TEXTMappingAdd(new cTEXTMapping("guest",tr("guest")));
+ TEXTMappingAdd(new cTEXTMapping("presenter",tr("presenter")));
+ TEXTMappingAdd(new cTEXTMapping("producer",tr("producer")));
+ TEXTMappingAdd(new cTEXTMapping("writer",tr("writer")));
+ TEXTMappingAdd(new cTEXTMapping("review",tr("review")));
+}
+
+cPluginXmltv2vdr::~cPluginXmltv2vdr()
+{
+ // Clean up after yourself!
+}
+
+const char *cPluginXmltv2vdr::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return NULL;
+}
+
+bool cPluginXmltv2vdr::ProcessArgs(int UNUSED(argc), char *UNUSED(argv[]))
+{
+ // Implement command line argument processing here if applicable.
+ return true;
+}
+
+bool cPluginXmltv2vdr::Initialize(void)
+{
+ // Initialize any background activities the plugin shall perform.
+ return true;
+}
+
+bool cPluginXmltv2vdr::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ ReadInEPGSources();
+ return true;
+}
+
+void cPluginXmltv2vdr::Stop(void)
+{
+ // Stop any background activities the plugin is performing.
+ removeepgsources();
+ removeepgmappings();
+ removetextmappings();
+}
+
+void cPluginXmltv2vdr::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+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))
+ {
+ executeepgsources();
+ last_exectime_t=exectime_t;
+ }
+}
+
+cString cPluginXmltv2vdr::Active(void)
+{
+ // Return a message string if shutdown should be postponed
+ if (epgsourcesactive())
+ {
+ return tr("xmltv2vdr plugin still working");
+ }
+ return NULL;
+}
+
+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;
+ }
+}
+
+const char *cPluginXmltv2vdr::MainMenuEntry(void)
+{
+ // Return a main menu entry
+ return NULL;
+}
+
+cOsdObject *cPluginXmltv2vdr::MainMenuAction(void)
+{
+ // Perform the action when selected from the main VDR menu.
+ return NULL;
+}
+
+cMenuSetupPage *cPluginXmltv2vdr::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return new cMenuSetupXmltv2vdr(this);
+}
+
+bool cPluginXmltv2vdr::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ if (!strncasecmp(Name,"channel",7))
+ {
+ if (strlen(Name)<10) return false;
+
+ cEPGMapping *map = new cEPGMapping(&Name[8],Value);
+ epgmappings.Add(map);
+ }
+ else if (!strncasecmp(Name,"textmap",7))
+ {
+ if (strlen(Name)<10) return false;
+ cTEXTMapping *textmap=TEXTMapping(&Name[8]);
+ if (textmap)
+ {
+ textmap->ChangeValue(Value);
+ }
+ else
+ {
+ cTEXTMapping *textmap = new cTEXTMapping(&Name[8],Value);
+ textmappings.Add(textmap);
+ }
+ }
+ else if (!strcasecmp(Name,"options.exectime"))
+ {
+ SetExecTime(atoi(Value));
+ }
+ else if (!strcasecmp(Name,"options.wakeup"))
+ {
+ wakeup=atoi(Value);
+ }
+ else return false;
+ return true;
+}
+
+bool cPluginXmltv2vdr::Service(const char *UNUSED(Id), void *UNUSED(Data))
+{
+ // Handle custom service requests from other plugins
+ return false;
+}
+
+const char **cPluginXmltv2vdr::SVDRPHelpPages(void)
+{
+ // Returns help text
+ static const char *HelpPages[]=
+ {
+ "UPDT\n"
+ " Start epg update",
+ NULL
+ };
+ return HelpPages;
+}
+
+cString cPluginXmltv2vdr::SVDRPCommand(const char *Command, const char *UNUSED(Option), int &ReplyCode)
+{
+ // Process SVDRP commands
+
+ cString output;
+ if (!strcasecmp(Command,"UPDT"))
+ {
+ if (!epgsources.Count())
+ {
+ ReplyCode=550;
+ output="No epg sources installed\n";
+ }
+ else
+ {
+ if (executeepgsources())
+ {
+ ReplyCode=250;
+ output="Update started\n";
+ }
+ else
+ {
+ ReplyCode=550;
+ output="Update already running\n";
+ }
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+ return output;
+}
+
+VDRPLUGINCREATOR(cPluginXmltv2vdr) // Don't touch this!
diff --git a/xmltv2vdr.h b/xmltv2vdr.h
new file mode 100644
index 0000000..993e1e6
--- /dev/null
+++ b/xmltv2vdr.h
@@ -0,0 +1,201 @@
+/*
+ * xmltv2vdr.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef _XMLTV2VDR_H
+#define _XMLTV2VDR_H
+
+#define EPGSOURCES "/var/lib/epgsources"
+
+#include <vdr/plugin.h>
+#include "maps.h"
+#include "parse.h"
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = trNOOP ( "Imports xmltv epg into vdr" );
+
+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 cEPGExecute : public cThread
+{
+private:
+ const char *name;
+ cParse *parse;
+ bool ready2parse;
+ bool pipe;
+ int daysinadvance;
+ int daysmax;
+ bool ReadConfig();
+ cEPGChannels channels;
+public:
+ cEPGExecute(const char *Name,cEPGMappings *Maps,cTEXTMappings *Texts);
+ ~cEPGExecute();
+ cEPGChannels *GetChannelList()
+ {
+ return &channels;
+ }
+ int GetDaysMax()
+ {
+ return daysmax;
+ }
+ int GetDaysInAdvance()
+ {
+ return daysinadvance;
+ }
+ void SetDaysInAdvance(int NewDaysInAdvance)
+ {
+ daysinadvance=NewDaysInAdvance;
+ }
+ void SetChannelSelection(int *Selection);
+ virtual void Action();
+ void Stop()
+ {
+ Cancel(3);
+ }
+};
+
+class cEPGSource : public cListObject
+{
+private:
+ const char *name;
+ cEPGExecute exec;
+public:
+ cEPGSource(const char *Name,cEPGMappings *Maps,cTEXTMappings *Texts);
+ ~cEPGSource();
+ bool Execute();
+ void Store(void);
+ cEPGChannels *ChannelList()
+ {
+ return exec.GetChannelList();
+ }
+ int DaysMax()
+ {
+ return exec.GetDaysMax();
+ }
+ int DaysInAdvance()
+ {
+ return exec.GetDaysInAdvance();
+ }
+ const char *Name()
+ {
+ return name;
+ }
+ void ChangeDaysInAdvance(int NewDaysInAdvance)
+ {
+ exec.SetDaysInAdvance(NewDaysInAdvance);
+ }
+ void ChangeChannelSelection(int *Selection)
+ {
+ exec.SetChannelSelection(Selection);
+ }
+ bool Active()
+ {
+ return exec.Active();
+ }
+};
+
+class cEPGSources : public cList<cEPGSource> {};
+
+class cPluginXmltv2vdr : public cPlugin
+{
+private:
+ cEPGMappings epgmappings;
+ cEPGSources epgsources;
+ cTEXTMappings textmappings;
+ void removeepgsources();
+ void removeepgmappings();
+ void removetextmappings();
+ bool executeepgsources();
+ bool epgsourcesactive();
+ bool epgsourceexists(const char *name);
+ int exectime;
+ time_t exectime_t,last_exectime_t;
+public:
+ int ExecTime()
+ {
+ return exectime;
+ }
+ void SetExecTime(int ExecTime);
+ int wakeup;
+ void ReadInEPGSources(bool Reload=false);
+ int EPGSourceCount()
+ {
+ return epgsources.Count();
+ }
+ cEPGSource *EPGSource(int Index)
+ {
+ return epgsources.Get(Index);
+ }
+ int EPGMappingCount()
+ {
+ return epgmappings.Count();
+ }
+ cEPGMapping *EPGMapping(int Index)
+ {
+ return epgmappings.Get(Index);
+ }
+ cEPGMapping *EPGMapping(const char *ChannelName);
+ void EPGMappingAdd(cEPGMapping *Map)
+ {
+ epgmappings.Add(Map);
+ }
+ cTEXTMapping *TEXTMapping(const char *Name);
+ void TEXTMappingAdd(cTEXTMapping *TextMap)
+ {
+ textmappings.Add(TextMap);
+ }
+ cPluginXmltv2vdr ( void );
+ virtual ~cPluginXmltv2vdr();
+ virtual const char *Version ( void )
+ {
+ return VERSION;
+ }
+ virtual const char *Description ( void )
+ {
+ return DESCRIPTION;
+ }
+ virtual const char *CommandLineHelp ( void );
+ virtual bool ProcessArgs ( int argc, char *argv[] );
+ virtual bool Initialize ( void );
+ virtual bool Start ( void );
+ virtual void Stop ( void );
+ virtual void Housekeeping ( void );
+ virtual void MainThreadHook ( void );
+ virtual cString Active ( void );
+ virtual time_t WakeupTime ( void );
+ virtual const char *MainMenuEntry ( void );
+ virtual cOsdObject *MainMenuAction ( void );
+ virtual cMenuSetupPage *SetupMenu ( void );
+ virtual bool SetupParse ( const char *Name, const char *Value );
+ virtual bool Service ( const char *Id, void *Data = NULL );
+ virtual const char **SVDRPHelpPages ( void );
+ virtual cString SVDRPCommand ( const char *Command, const char *Option, int &ReplyCode );
+};
+
+#endif