From 83cf2436ce3f2ed9ce4e0a0847b10d5144907061 Mon Sep 17 00:00:00 2001 From: Jochen Dolze Date: Wed, 29 Dec 2010 16:54:30 +0100 Subject: First commit --- HISTORY | 6 + Makefile | 120 +++++++ README | 17 + extpipe.cpp | 131 ++++++++ extpipe.h | 23 ++ maps.cpp | 212 ++++++++++++ maps.h | 107 ++++++ parse.cpp | 647 ++++++++++++++++++++++++++++++++++++ parse.h | 146 ++++++++ po/de_DE.po | 164 +++++++++ setup.cpp | 1034 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.h | 119 +++++++ xmltv2vdr.cpp | 641 +++++++++++++++++++++++++++++++++++ xmltv2vdr.h | 201 +++++++++++ 14 files changed, 3568 insertions(+) create mode 100644 HISTORY create mode 100644 Makefile create mode 100644 README create mode 100644 extpipe.cpp create mode 100644 extpipe.h create mode 100644 maps.cpp create mode 100644 maps.h create mode 100644 parse.cpp create mode 100644 parse.h create mode 100644 po/de_DE.po create mode 100644 setup.cpp create mode 100644 setup.h create mode 100644 xmltv2vdr.cpp create mode 100644 xmltv2vdr.h 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='' -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 + +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 +#include +#include +#include +#include +#include +#include +#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 +#include + +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 + +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; iNumber(); + } + 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; iGetChannelID()) + { + 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; iGetChannelID()) + { + 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 + +// 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 {}; + +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 {}; + +#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 +#include +#include + +#include +#include + +#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) && (starttimeNumChannelIDs(); 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 +#include +#include +#include + +#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: \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 \n" +"Language-Team: \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 + +#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; iEPGSourceCount(); 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; iEPGMapping(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; iEPGSourceCount(); i++) + { + cEPGSource *epgsrc=baseplugin->EPGSource(i); + if (epgsrc) + { + cEPGChannels *channellist=epgsrc->ChannelList(); + if (channellist) + { + for (int x=0; xCount(); x++) + { + if (channellist->Get(x)->InUse()) + { + bool found=false; + for (int t=0; tGet(x)->Name())) found=true; + } + if (!found) + { + channels.Append(strdup(channellist->Get(x)->Name())); + } + + } + } + } + } + } + channels.Sort(); + + for (int i=0; iEPGMapping(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; iCount(); 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; iCount(); 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; iNumChannelIDs(); i++) + { + cString ChannelID = epgmap->ChannelIDs()[i].ToString(); + value=strcatrealloc(value,*ChannelID); + if (iNumChannelIDs()-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; iEPGSourceCount(); i++) + { + cEPGSource *epgsrc=baseplugin->EPGSource(i); + if (epgsrc) + { + cEPGChannels *channellist=epgsrc->ChannelList(); + if (channellist) + { + for (int x=0; xCount(); x++) + { + if (!strcmp(channellist->Get(x)->Name(),channel)) + { + if (channellist->Get(x)->InUse()) + { + if (epgsrc->DaysMax()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; iNumChannelIDs(); 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; iNumChannelIDs(); 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; iNumChannelIDs(); i++) + { + cString ChannelID = map->ChannelIDs()[i].ToString(); + value=strcatrealloc(value,*ChannelID); + if (iNumChannelIDs()-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; iEPGSourceCount(); i++) + { + cEPGSource *epgsrc=baseplugin->EPGSource(i); + if (epgsrc) + { + cEPGChannels *channellist=epgsrc->ChannelList(); + if (channellist) + { + for (int x=0; xCount(); x++) + { + if (!strcmp(channellist->Get(x)->Name(),channel)) + { + if (epgsrc->DaysInAdvance()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; iEPGMappingCount(); i++) + { + if (channel2ignore && !strcmp(baseplugin->EPGMapping(i)->ChannelName(),channel2ignore)) continue; + for (int x=0; xEPGMapping(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 +#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 +#include +#include +#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; iSetUsage(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; iCount(); 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 +#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 {}; + +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 {}; + +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 -- cgit v1.2.3