diff options
-rw-r--r-- | HISTORY | 6 | ||||
-rw-r--r-- | Makefile | 120 | ||||
-rw-r--r-- | README | 17 | ||||
-rw-r--r-- | extpipe.cpp | 131 | ||||
-rw-r--r-- | extpipe.h | 23 | ||||
-rw-r--r-- | maps.cpp | 212 | ||||
-rw-r--r-- | maps.h | 107 | ||||
-rw-r--r-- | parse.cpp | 647 | ||||
-rw-r--r-- | parse.h | 146 | ||||
-rw-r--r-- | po/de_DE.po | 164 | ||||
-rw-r--r-- | setup.cpp | 1034 | ||||
-rw-r--r-- | setup.h | 119 | ||||
-rw-r--r-- | xmltv2vdr.cpp | 641 | ||||
-rw-r--r-- | xmltv2vdr.h | 201 |
14 files changed, 3568 insertions, 0 deletions
@@ -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 @@ -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©) +{ + 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--; + } +} @@ -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©); + 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); +} @@ -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; +} @@ -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 |