[vdr-plugin-vompserver] 01/05: New upstream version 0.5.0

Tobias Grimm tiber-guest at moszumanska.debian.org
Sat Feb 17 14:52:50 UTC 2018


This is an automated email from the git hooks/post-receive script.

tiber-guest pushed a commit to branch master
in repository vdr-plugin-vompserver.

commit baa7efb53be86a00970ac6bf4e20e76feb87d13f
Author: Tobias Grimm <git at e-tobi.net>
Date:   Sat Feb 17 15:49:39 2018 +0100

    New upstream version 0.5.0
---
 Makefile               |   7 +-
 config.c               |   7 +-
 mediafile.c            |  10 +-
 mvpreceiver.c          |  15 +-
 mvpreceiver.h          |   7 +-
 mvpserver.c            |  60 ++++-
 mvpserver.h            |   4 +
 picturereader.c        | 397 ++++++++++++++++++++++++++++
 picturereader.h        |  75 ++++++
 recplayer.c            |  12 +-
 recplayer.h            |   6 +-
 ringbuffer.c           |   2 +-
 ringbuffer.h           |   2 +-
 services/readme.txt    |   3 +
 services/scraper2vdr.h | 196 ++++++++++++++
 udpreplier.c           |   4 +-
 vdrcommand.h           |  13 +-
 vomp.conf.sample       |  10 +
 vompclient.c           |  62 ++---
 vompclient.h           |  19 +-
 vompclientrrproc.c     | 702 ++++++++++++++++++++++++++++++++++++++++++-------
 vompclientrrproc.h     |  15 +-
 vompserver.c           |  19 +-
 23 files changed, 1465 insertions(+), 182 deletions(-)

diff --git a/Makefile b/Makefile
index 9c99e28..b6a0d99 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri
 ### The directory environment:
 
 # Use package data if installed...otherwise assume we're under the VDR source directory:
-PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc))
+PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr))
 LIBDIR = $(call PKGCFG,libdir)
 LOCDIR = $(call PKGCFG,locdir)
 PLGCFG = $(call PKGCFG,plgcfg)
@@ -53,7 +53,7 @@ SOFILE = libvdr-$(PLUGIN).so
 
 INCLUDES +=
 
-DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -D__STL_CONFIG_H
 
 # VOMP-INSERT
 DEFINES += -DVOMPSERVER
@@ -68,7 +68,8 @@ OBJS += dsock.o mvpserver.o udpreplier.o bootpd.o tftpd.o i18n.o vompclient.o tc
                    ringbuffer.o mvprelay.o vompclientrrproc.o \
                    config.o log.o thread.o tftpclient.o \
                    media.o responsepacket.o \
-                   mediafile.o mediaplayer.o servermediafile.o serialize.o medialauncher.o
+                   mediafile.o mediaplayer.o servermediafile.o serialize.o medialauncher.o \
+                   picturereader.o
 
 OBJS2 = recplayer.o mvpreceiver.o
 # END-VOMP-INSERT
diff --git a/config.c b/config.c
index 3866abc..f4a5e01 100644
--- a/config.c
+++ b/config.c
@@ -137,7 +137,7 @@ FILE* Config::copyToHere(long position)
 
   while (newPos < position)
   {
-    fgets(buffer, BUFFER_LENGTH-1, file);
+    if (!fgets(buffer, BUFFER_LENGTH-1, file)) break;
     fputs(buffer, newFile);
     newPos += strlen(buffer);
   }
@@ -183,7 +183,8 @@ int Config::deleteValue(const char* section, char* key)
   }
 
   FILE* newFile = copyToHere(ftell(file) - lastLineLength);
-  fgets(buffer, BUFFER_LENGTH-1, file);
+
+  if (  fgets(buffer, BUFFER_LENGTH-1, file)  );
 
   return copyRest(newFile);
 }
@@ -226,7 +227,7 @@ int Config::setValueString(const char* section, const char* key, const char* new
         return 0;
       }
 
-      fgets(buffer, BUFFER_LENGTH-1, file);
+      if (  fgets(buffer, BUFFER_LENGTH-1, file)  );
       fprintf(newFile, "%s = %s\n", key, newValue);
       return copyRest(newFile);
     }
diff --git a/mediafile.c b/mediafile.c
index 93bbfca..a148e5d 100644
--- a/mediafile.c
+++ b/mediafile.c
@@ -121,13 +121,19 @@ MediaList* MediaFile::getMediaList(const MediaURI * parent){
   const char *dirname=parent->getName();
   //open the directory and read out the entries
   DIR *d=opendir(dirname);
+  if (d == NULL) return rt;
   struct dirent *e;
+
+  /* readdir_r is now deprecated in favour of readdir (which is effectively thread safe)
   union { // according to "The GNU C Library Reference Manual"
     struct dirent d;
     char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
     } u;
 
   while (d != NULL && (readdir_r(d,&u.d,&e) == 0) && e != NULL) {
+  */
+
+  while (e = readdir(d)) {
     const char * fname=e->d_name;
     if ( fname == NULL) continue;
     if (strcmp(fname,".") == 0) continue;
@@ -141,9 +147,9 @@ MediaList* MediaFile::getMediaList(const MediaURI * parent){
       if (m) delete m;
     }
   }
-  if (d != NULL) closedir(d);
+  closedir(d);
   return rt;
-  }
+}
 
 
 int MediaFile::openMedium(ULONG channel, const MediaURI * uri, ULLONG * size, ULONG xsize, ULONG ysize) {
diff --git a/mvpreceiver.c b/mvpreceiver.c
index f4259a7..3ffb4b7 100644
--- a/mvpreceiver.c
+++ b/mvpreceiver.c
@@ -2,7 +2,7 @@
 
 int MVPReceiver::numMVPReceivers = 0;
 
-MVPReceiver* MVPReceiver::create(cChannel* channel, int priority)
+MVPReceiver* MVPReceiver::create(const cChannel* channel, int priority)
 {
 #if VDRVERSNUM < 10500
   bool NeedsDetachReceivers;
@@ -34,7 +34,7 @@ MVPReceiver* MVPReceiver::create(cChannel* channel, int priority)
   return m;
 }
 
-MVPReceiver::MVPReceiver(cChannel* channel, cDevice* device)
+MVPReceiver::MVPReceiver(const cChannel* channel, cDevice* device)
 #if VDRVERSNUM < 10300
 : cReceiver(channel->Ca(), 0, 7, channel->Vpid(), channel->Ppid(), channel->Apid1(), channel->Apid2(), channel->Dpid1(), channel->Dpid2(), channel->Tpid())
 #elif VDRVERSNUM < 10500
@@ -57,7 +57,7 @@ MVPReceiver::MVPReceiver(cChannel* channel, cDevice* device)
 
 //  logger->log("MVPReceiver", Log::DEBUG, "Channel has VPID %i APID %i", channel->Vpid(), channel->Apid(0));
 
-  if (!processed.init(1000000)) return;
+  if (!processed.init(6000000)) return; // Ringbuffer increased for better performance 
   pthread_mutex_init(&processedRingLock, NULL);
 
   // OK
@@ -113,6 +113,7 @@ void MVPReceiver::detachMVPReceiver()
   Detach();
 }
 
+
 void MVPReceiver::Receive(UCHAR* data, int length)
 {
   pthread_mutex_lock(&processedRingLock);
@@ -120,6 +121,14 @@ void MVPReceiver::Receive(UCHAR* data, int length)
   if (processed.getContent() > streamChunkSize) threadSignal();
   pthread_mutex_unlock(&processedRingLock);
 }
+void MVPReceiver::Receive(const UCHAR* data, int length)
+{
+  pthread_mutex_lock(&processedRingLock);
+  processed.put(data, length);
+  if (processed.getContent() > streamChunkSize) threadSignal();
+  pthread_mutex_unlock(&processedRingLock);
+}
+
 
 void MVPReceiver::threadMethod()
 {
diff --git a/mvpreceiver.h b/mvpreceiver.h
index e1acf39..747e925 100644
--- a/mvpreceiver.h
+++ b/mvpreceiver.h
@@ -34,14 +34,14 @@
 class MVPReceiver : public cReceiver, public Thread
 {
   public:
-    static MVPReceiver* create(cChannel*, int priority);
+    static MVPReceiver* create(const cChannel*, int priority);
     virtual ~MVPReceiver();
     int init(TCP* tcp, ULONG streamID);
     bool isVdrActivated();
     void detachMVPReceiver();
 
   private:
-    MVPReceiver(cChannel* channel, cDevice* device);
+    MVPReceiver(const cChannel* channel, cDevice* device);
 
     Log* logger;
     bool vdrActivated;
@@ -56,7 +56,8 @@ class MVPReceiver : public cReceiver, public Thread
 
     // cReciever stuff
     void Activate(bool On);
-    void Receive(UCHAR *Data, int Length);
+    void Receive(UCHAR *Data, int Length); // VDR 2.2.0
+    void Receive(const UCHAR *Data, int Length); // > VDR 2.2.0
     void sendStreamEnd();
 
     static int numMVPReceivers;
diff --git a/mvpserver.c b/mvpserver.c
index 8ae63b8..bbf8fbd 100644
--- a/mvpserver.c
+++ b/mvpserver.c
@@ -30,10 +30,18 @@ MVPServer::MVPServer()
   // MH in case anbody has a better position :-)
   pthread_mutex_init(&threadClientMutex, NULL);
   tcpServerPort = 0;
+  logoDir = NULL;
+  resourceDir = NULL;
+  imageDir = NULL;
+  cacheDir = NULL;
 }
 
 MVPServer::~MVPServer()
 {
+  if (logoDir) delete[] logoDir;
+  if (resourceDir) delete[] resourceDir;
+  if (imageDir) delete[] imageDir;
+  if (cacheDir) delete[] cacheDir;
   stop();
 }
 
@@ -92,6 +100,56 @@ int MVPServer::run(char* tconfigDir)
     dsyslog("VOMP: Logging disabled");
   }
 
+  const char *bigresdir = cPlugin::ResourceDirectory();  
+  const char *bigcachedir = cPlugin::CacheDirectory();  
+  // get logo directory
+  logoDir =  config.getValueString("General", "Channel logo directory");
+  
+  if (logoDir) 
+  {
+    log.log("Main", Log::INFO, "LogoDir set %s", logoDir);
+  } else {
+    if (bigresdir) {
+	logoDir = new char[strlen(bigresdir)+1+7];
+	sprintf(logoDir,"%s/logos/",bigresdir);    
+	log.log("Main", Log::INFO, "No LogoDir set, default %s",logoDir);
+     } else {
+	log.log("Main", Log::INFO, "No LogoDir set, no res dir");
+     }
+        
+  }
+
+  // get epg Image directory
+  imageDir =  config.getValueString("General", "Epg image directory");
+  
+  if (imageDir) 
+  {
+    log.log("Main", Log::INFO, "ImageDir set %s", imageDir);
+  } else {
+    if (bigcachedir) {
+	imageDir = new char[strlen(bigcachedir)+1+11+3];
+	sprintf(imageDir,"%s/../epgimages/",bigcachedir);    
+	log.log("Main", Log::INFO, "No ImageDir set, default %s",imageDir);
+    } else {
+      	log.log("Main", Log::INFO, "No ImageDir set, no cache dir");
+    }
+  }
+
+  if (bigresdir) {
+    resourceDir = new char[strlen(bigresdir)+1];
+    strcpy(resourceDir,bigresdir);
+    log.log("Main", Log::INFO, "Resource directory is  %s",bigresdir);
+  } else {
+    log.log("Main", Log::INFO, "Resource directory is  not set");
+  }
+  
+  if (bigcachedir) {
+    cacheDir = new char[strlen(bigcachedir)+1];
+    strcpy(cacheDir,bigcachedir);
+    log.log("Main", Log::INFO, "Cache directory is  %s",bigcachedir);
+  } else {
+    log.log("Main", Log::INFO, "Cache directory is  not set");
+  }
   // Get UDP port number for discovery service
 
   int fail = 1;
@@ -272,7 +330,7 @@ void MVPServer::threadMethod()
   while(1)
   {
     clientSocket = accept(listeningSocket,(struct sockaddr *)&address, &length);
-    VompClient* m = new VompClient(&config, configDir, clientSocket);
+    VompClient* m = new VompClient(&config, configDir, logoDir, resourceDir, imageDir, cacheDir, clientSocket);
     m->run();
   }
 }
diff --git a/mvpserver.h b/mvpserver.h
index 80cc211..ed29c65 100644
--- a/mvpserver.h
+++ b/mvpserver.h
@@ -54,6 +54,10 @@ class MVPServer : public Thread
     MVPRelay mvprelay;
     int listeningSocket;
     char* configDir;
+    char* logoDir;
+    char* imageDir;
+    char* resourceDir;
+    char* cacheDir;
     USHORT tcpServerPort;
 };
 
diff --git a/picturereader.c b/picturereader.c
new file mode 100644
index 0000000..59353b7
--- /dev/null
+++ b/picturereader.c
@@ -0,0 +1,397 @@
+#include "picturereader.h"
+#include <vdr/plugin.h>
+#include <vdr/channels.h>
+#include <sstream>
+#include <algorithm>
+
+
+PictureReader::PictureReader(VompClient *client)
+{
+  logger = Log::getInstance();
+  inittedOK = 0;
+  tcp = NULL;
+  x = client;
+
+  pthread_mutex_init(&pictureLock, NULL);
+}  
+
+int PictureReader::init(TCP* ttcp)
+{
+  tcp = ttcp;
+  threadStart();
+
+  return inittedOK;
+}
+
+PictureReader::~PictureReader()
+{
+   threadStop();
+}
+
+void PictureReader::addTVMediaRequest(TVMediaRequest& req)
+{
+    logger->log("PictRead",Log::DEBUG,"Got TVMediaRequest, signal thread!");
+
+    pthread_mutex_lock(&pictureLock);
+    pictures.push(req);
+    threadSignal(); // Signal, that we have something to do!!!
+    pthread_mutex_unlock(&pictureLock);
+}
+
+bool PictureReader::epgImageExists(int event)
+{
+    if (x->imageDir) {
+        std::ostringstream file;
+        file<< std::string(x->imageDir)<< event << std::string(".jpg");
+	if (!access(file.str().c_str() ,F_OK))
+	{
+	    return true;
+	}
+
+	std::ostringstream file2;
+	file2 << std::string(x->imageDir) << event << std::string("_0.jpg");
+	if (!access(file2.str().c_str() ,F_OK))
+	{
+	    return true;
+	}
+    } else if (x->cacheDir) {
+
+        std::ostringstream file;
+        file<<std::string(x->cacheDir)<<std::string("/../epgimages/")
+    		<< event << std::string(".jpg");
+	if (!access(file.str().c_str() ,F_OK))
+	{
+	    return true;
+	}
+	std::ostringstream file2;
+	file2<<std::string(x->cacheDir)<<std::string("/../epgimages/")<<
+	     event <<std::string("_0.jpg");
+	if (!access(file2.str().c_str() ,F_OK))
+	{
+	    return true;
+	}
+    }
+    return false; 
+}
+
+std::string PictureReader::getPictName(TVMediaRequest & req)
+{
+   logger->log("PictRead",Log::DEBUG,
+	"Request %d %d;  %d %d %d %d",req.primary_id,req.secondary_id,
+	 req.type,req.type_pict,
+	req.container,req.container_member);
+
+   switch (req.type) {
+   case 0: { //serie
+      if (series.seriesId != (int)req.primary_id ||
+          series.episodeId != (int)req.secondary_id) {
+          series.actors.clear();
+          series.posters.clear();
+          series.banners.clear();
+          series.fanarts.clear();
+
+          series.seriesId = req.primary_id;
+          series.episodeId = req.secondary_id;
+          x->scraper->Service("GetSeries",&series);
+      }
+      if (req.type_pict == 0) {
+        switch (req.container) {
+        case 0: {
+           return series.episode.episodeImage.path;
+        } break;
+        case 1: {
+          if (series.actors.size()>req.container_member) {
+        	return series.actors[req.container_member].actorThumb.path;
+          }
+        } break;
+        case 2: {
+          if (series.posters.size()>req.container_member) {
+        	return series.posters[req.container_member].path;
+          }
+        } break;
+        case 3: {
+          if (series.banners.size()>req.container_member) {
+        	return series.banners[req.container_member].path;
+          }
+        } break;
+        case 4: {
+          if (series.fanarts.size()>req.container_member) {
+        	return series.fanarts[req.container_member].path;
+          }
+        } break;
+        case 5: {
+           return series.seasonPoster.path;
+        } break;
+        default: {
+           return std::string("");
+           } break;
+        };
+      } else if (req.type_pict == 1 && series.posters.size()) { //poster
+           std::string str=series.posters[0].path;
+           size_t  pos=str.rfind('/');
+           if (pos!=std::string::npos) {
+              str.resize(pos);
+              str=str+"poster_thumb.jpg";
+              return str;
+           }
+        } else if (req.type_pict == 2) { //poster
+           std::string str=series.seasonPoster.path;
+           size_t  pos=str.rfind('/');
+           if (pos!=std::string::npos) {
+              str.resize(pos);
+              std::ostringstream out;
+              out << str << "season_" <<series.episode.season <<"_thumb.jpg";
+              return out.str();
+           }
+        } 
+       return std::string("");
+   } break;
+   case 1: { //movie
+      if (movie.movieId != (int)req.primary_id ) {
+          movie.actors.clear();
+          movie.movieId = req.primary_id;
+          x->scraper->Service("GetMovie",&movie);
+      }
+      if (req.type_pict == 0) {
+
+        switch (req.container) {
+        case 0: {
+           return movie.poster.path;
+        } break;
+        case 1: {
+           return movie.fanart.path;
+        } break;
+        case 2: {
+           return movie.collectionPoster.path;
+        } break;
+        case 3: {
+           return movie.collectionFanart.path;
+        } break;
+        case 4: {
+          if (movie.actors.size()>req.container_member) {
+        	return movie.actors[req.container_member].actorThumb.path;
+          }
+        } break;
+        default: {
+           return std::string("");
+           } break;
+        };
+    } else if (req.type_pict == 1) { //poster
+        std::string str=movie.poster.path;
+        size_t  pos=str.rfind('/');
+        if (pos!=std::string::npos) {
+           str.resize(pos);
+           str=str+"poster_thumb.jpg";
+           return str;
+        }
+    } 
+    return std::string("");
+      
+   
+   } break;
+   case 3: { // I do not know
+   // First get the recording
+#if VDRVERSNUM >= 20301
+      LOCK_RECORDINGS_READ;
+      const cRecordings* tRecordings = Recordings;
+#else
+      cThreadLock RecordingsLock(&Recordings);
+      cRecordings* tRecordings = &Recordings;
+#endif
+      const cRecording *recording = tRecordings->GetByName((char*) req.primary_name.c_str());
+      ScraperGetPosterThumb getter;
+      getter.recording = recording;
+      getter.event = NULL;
+      if (x->scraper && recording) {
+        x->scraper->Service("GetPosterThumb",&getter);
+        return getter.poster.path;
+      } else {
+         return std::string("");
+      }
+   }; break;
+   case 4: { // I do not know
+   // First get the schedules
+
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  const cChannels* tChannels = Channels;
+#else
+  cChannels* tChannels = &Channels;
+#endif
+
+
+#if VDRVERSNUM < 10300
+      cMutexLock MutexLock;
+      const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
+#elif VDRVERSNUM < 20301
+      cSchedulesLock MutexLock;
+      const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
+#else
+      LOCK_SCHEDULES_READ;
+      const cSchedules *tSchedules = Schedules;
+#endif
+
+      const cSchedule *Schedule = NULL;
+      if (tSchedules)
+      {
+        const cChannel* channel = tChannels->GetByChannelID(tChannelID::FromString(req.primary_name.c_str()));
+        Schedule = tSchedules->GetSchedule(channel);
+      }
+      const cEvent *event = NULL;
+      if (Schedule) event=Schedule->GetEvent(req.primary_id);
+      ScraperGetPosterThumb getter;
+      getter.event = event;
+      getter.recording = NULL;
+
+      if (x->scraper && event) {
+        x->scraper->Service("GetPosterThumb",&getter);
+        if (getter.poster.width) return getter.poster.path;
+      }
+      if (x->imageDir) {
+        std::ostringstream file;
+        file<< std::string(x->imageDir)<< req.primary_id << std::string(".jpg");
+	if (!access(file.str().c_str() ,F_OK))
+	{
+	    return file.str();
+	}
+
+	std::ostringstream file2;
+	file2 << std::string(x->imageDir) << req.primary_id << std::string("_0.jpg");
+	if (!access(file2.str().c_str() ,F_OK))
+	{
+	    return file2.str();
+	}
+      } else if (x->cacheDir) {
+
+        std::ostringstream file;
+        file<<std::string(x->cacheDir)<<std::string("/../epgimages/")
+    		<<req.primary_id << std::string(".jpg");
+	if (!access(file.str().c_str() ,F_OK))
+	{
+	    return file.str();
+	}
+	std::ostringstream file2;
+	file2<<std::string(x->cacheDir)<<std::string("/../epgimages/")<<
+	     req.primary_id<<std::string("_0.jpg");
+	if (!access(file2.str().c_str() ,F_OK))
+	{
+	    return file2.str();
+	}
+      } 
+      return std::string("");
+   }; break;
+   case 5: { // Channel logo
+	std::transform(req.primary_name.begin(),req.primary_name.end(),
+	    req.primary_name.begin(),::tolower);
+	if (x->logoDir) {
+	   std::string file=std::string(x->logoDir)+req.primary_name+std::string(".png");
+	   if (!access(file.c_str() ,F_OK))
+	   {
+	      return file;
+	   }
+	}
+	// if noopacity is there steal the logos
+	if (x->resourceDir) {
+	    std::string file=std::string(x->resourceDir)
+				+std::string("/skinnopacity/logos/")+req.primary_name+std::string(".png");
+	    if (!access(file.c_str() ,F_OK))
+	    {
+		return file;
+	    }
+	}
+	return std::string("");
+
+   
+   }; break;
+   default:
+     return std::string("");
+     break;
+   };
+   return std::string("");
+   
+}
+
+
+void PictureReader::threadMethod()
+{
+  ULONG *p;
+  ULONG headerLength = sizeof(ULONG) * 4;
+  UCHAR buffer[headerLength];
+
+//   threadSetKillable(); ??
+
+  logger->log("PictRead",Log::DEBUG,"PictureReaderThread started");
+  while(1)
+  {
+    threadLock();
+    threadWaitForSignal();
+    threadUnlock();
+    threadCheckExit();
+    bool newpicture;
+    logger->log("PictRead",Log::DEBUG,"Thread was signaled, wake up");
+
+    do
+    {
+      newpicture = false;
+      TVMediaRequest req;
+      pthread_mutex_lock(&pictureLock);
+      if (!pictures.empty()) {
+         newpicture = true;
+         req = pictures.front();
+         pictures.pop();
+      }
+      pthread_mutex_unlock(&pictureLock);
+      if (!newpicture) break;
+      std::string pictname = getPictName(req);
+      UCHAR * mem = NULL;
+      ULONG memsize = 0;
+      ULONG flag = 2;
+      logger->log("PictRead",Log::DEBUG,"Load Pict %s",pictname.c_str());
+
+      
+      if (pictname.length()) {
+         struct stat st;
+         ULONG filesize = 0 ;
+
+         stat(pictname.c_str(), &st);
+         filesize = st.st_size;
+         memsize = filesize + headerLength;
+
+         if (memsize && memsize < 1000000) { // No pictures over 1 MB
+            mem = (UCHAR*)malloc(memsize);
+            if (mem) {
+        	FILE * file=fopen(pictname.c_str(),"r");
+
+        	if (file) {
+        	    size_t size=fread(mem+headerLength,1,filesize,file);
+
+        	    fclose(file);
+        	    if (size!=filesize) memsize=headerLength; // error
+        	    else flag = 0;
+        	}
+             }
+           } 
+       } 
+       if (!mem) {
+          mem = buffer;
+       }
+
+         
+    
+      p = (ULONG*)&mem[0]; *p = htonl(5); // stream channel
+      p = (ULONG*)&mem[4]; *p = htonl(req.streamID);
+      p = (ULONG*)&mem[8]; *p = htonl(flag); // here insert flag: 0 = ok, data follows
+      p = (ULONG*)&mem[12]; *p = htonl(memsize);
+
+      if (!tcp->sendPacket(mem, memsize + headerLength)) {
+          logger->log("PictRead",Log::DEBUG,"Sending Picture failed");
+      }
+
+      if (mem != buffer && mem) free(mem);
+
+    } while (newpicture);
+  }
+  logger->log("PictRead",Log::DEBUG,"PictureReaderThread ended");
+  
+}
+
diff --git a/picturereader.h b/picturereader.h
new file mode 100644
index 0000000..5cbbaec
--- /dev/null
+++ b/picturereader.h
@@ -0,0 +1,75 @@
+/*
+    Copyright 2004-2005 Chris Tallon, 2014 Marten Richter
+
+    This file is part of VOMP.
+
+    VOMP 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.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#ifndef PICTUREREADER_H
+#define PICTUREREADER_H
+
+#include "defines.h"
+#include "log.h"
+#include "thread.h"
+#include "tcp.h"
+#include "thread.h"
+#include "vompclient.h"
+#include "services/scraper2vdr.h"
+#include <queue>
+#include <string>
+
+struct TVMediaRequest{
+   ULONG streamID;
+   ULONG type;
+   ULONG primary_id;
+   ULONG secondary_id;
+   ULONG type_pict;
+   ULONG container;
+   ULONG container_member;
+   std::string primary_name;
+};
+
+class PictureReader : public Thread
+{
+  public:
+    PictureReader(VompClient * client);
+    virtual ~PictureReader();
+    int init(TCP* tcp);
+    void detachMVPReceiver();
+    void addTVMediaRequest(TVMediaRequest&);
+    bool epgImageExists(int event);
+
+  private:
+
+    std::string getPictName(TVMediaRequest&);
+
+    Log* logger;
+    int inittedOK;
+    pthread_mutex_t pictureLock; // needs outside locking
+    std::queue<TVMediaRequest> pictures;
+
+    TCP* tcp;
+    VompClient * x;
+    cSeries series;
+    cMovie movie;
+    
+  protected:
+    void threadMethod();
+};
+
+#endif
+
+
diff --git a/recplayer.c b/recplayer.c
index 9fac759..ce01e26 100644
--- a/recplayer.c
+++ b/recplayer.c
@@ -26,7 +26,7 @@
 
 #include <fcntl.h>
 
-RecPlayer::RecPlayer(cRecording* rec)
+RecPlayer::RecPlayer(const cRecording* rec)
 {
   log = Log::getInstance();
   file = NULL;
@@ -76,7 +76,7 @@ void RecPlayer::scan()
     segments[i] = new Segment();
     segments[i]->start = totalLength;
     fseek(file, 0, SEEK_END);
-    totalLength += ftell(file);
+    totalLength += ftello(file);
     totalFrames = indexFile->Last();
     log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
     segments[i]->end = totalLength;
@@ -132,7 +132,7 @@ ULONG RecPlayer::getLengthFrames()
 
 unsigned long RecPlayer::getBlock(unsigned char* buffer, ULLONG position, unsigned long amount)
 {
-  if ((amount > totalLength) || (amount > 500000))
+  if ((amount > totalLength) || (amount > 1000000))
   {
     log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
     return 0;
@@ -168,7 +168,7 @@ unsigned long RecPlayer::getBlock(unsigned char* buffer, ULLONG position, unsign
   ULONG yetToGet = amount;
   ULONG got = 0;
   ULONG getFromThisSegment = 0;
-  ULONG filePosition;
+  ULLONG filePosition;
 
   while(got < amount)
   {
@@ -186,7 +186,7 @@ unsigned long RecPlayer::getBlock(unsigned char* buffer, ULLONG position, unsign
       getFromThisSegment = segments[segmentNumber]->end - currentPosition;
 
     filePosition = currentPosition - segments[segmentNumber]->start;
-    fseek(file, filePosition, SEEK_SET);
+    fseeko(file, filePosition, SEEK_SET);
     if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem.
  
     // Tell linux not to bother keeping the data in the FS cache
@@ -206,7 +206,7 @@ ULLONG RecPlayer::getLastPosition()
   return lastPosition;
 }
 
-cRecording* RecPlayer::getCurrentRecording()
+const cRecording* RecPlayer::getCurrentRecording()
 {
   return recording;
 }
diff --git a/recplayer.h b/recplayer.h
index 95f310a..288e60d 100644
--- a/recplayer.h
+++ b/recplayer.h
@@ -37,14 +37,14 @@ class Segment
 class RecPlayer
 {
   public:
-    RecPlayer(cRecording* rec);
+    RecPlayer(const cRecording* rec);
     ~RecPlayer();
     ULLONG getLengthBytes();
     ULONG getLengthFrames();
     unsigned long getBlock(unsigned char* buffer, ULLONG position, unsigned long amount);
     int openFile(int index);
     ULLONG getLastPosition();
-    cRecording* getCurrentRecording();
+    const cRecording* getCurrentRecording();
     void scan();
     ULLONG positionFromFrameNumber(ULONG frameNumber);
     ULONG frameNumberFromPosition(ULLONG position);
@@ -52,7 +52,7 @@ class RecPlayer
 
   private:
     Log* log;
-    cRecording* recording;
+    const cRecording* recording;
     cIndexFile* indexFile;
     FILE* file;
     int fileOpen;
diff --git a/ringbuffer.c b/ringbuffer.c
index 20db41c..9de4be9 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -49,7 +49,7 @@ int Ringbuffer::init(size_t size)
   return 1;
 }
 
-int Ringbuffer::put(UCHAR* from, size_t amount)
+int Ringbuffer::put(const UCHAR* from, size_t amount)
 {
   if (amount > capacity) return 0;
 
diff --git a/ringbuffer.h b/ringbuffer.h
index c5a6e14..139eb72 100644
--- a/ringbuffer.h
+++ b/ringbuffer.h
@@ -33,7 +33,7 @@ class Ringbuffer
     Ringbuffer();
     ~Ringbuffer();
     int init(size_t size);
-    int put(UCHAR* from, size_t amount);
+    int put(const UCHAR* from, size_t amount);
     int get(UCHAR* to, size_t amount);
     int getContent();
 
diff --git a/services/readme.txt b/services/readme.txt
new file mode 100644
index 0000000..94d29c4
--- /dev/null
+++ b/services/readme.txt
@@ -0,0 +1,3 @@
+In this directories are API header files for calls to other plugins.
+Copyright by the authors of the respective plugins.
+* scraper2vdr by Louis Braun
\ No newline at end of file
diff --git a/services/scraper2vdr.h b/services/scraper2vdr.h
new file mode 100644
index 0000000..80a3374
--- /dev/null
+++ b/services/scraper2vdr.h
@@ -0,0 +1,196 @@
+#ifndef __SCRAPER2VDRSERVICES_H
+#define __SCRAPER2VDRSERVICES_H
+
+#include <string>
+#include <vector>
+#include <vdr/epg.h>
+#include <vdr/recording.h>
+
+enum tvType {
+    tSeries,
+    tMovie,
+    tNone,
+};
+
+/*********************************************************************
+* Helper Structures
+*********************************************************************/
+class cTvMedia {
+public:
+	cTvMedia(void) {
+		path = "";
+		width = height = 0;
+	};
+    std::string path;
+    int width;
+    int height;
+};
+
+class cEpisode {
+public:
+    cEpisode(void) {
+        number = 0;
+        season = 0;
+        name = "";
+        firstAired = "";
+        guestStars = "";
+        overview = "";
+        rating = 0.0;
+    };
+    int number;
+    int season;
+    std::string name;
+    std::string firstAired;
+    std::string guestStars;
+    std::string overview;
+    float rating;
+    cTvMedia episodeImage;
+};
+
+class cActor {
+public:
+    cActor(void) {
+        name = "";
+        role = "";
+    };
+    std::string name;
+    std::string role;
+    cTvMedia actorThumb;
+};
+
+/*********************************************************************
+* Data Structures for Service Calls
+*********************************************************************/
+
+// Data structure for service "GetEventType"
+class ScraperGetEventType {
+public:
+	ScraperGetEventType(void) {
+		event = NULL;
+        recording = NULL;
+		type = tNone;
+		movieId = 0;
+		seriesId = 0;
+		episodeId = 0;
+	};
+// in
+    const cEvent *event;             // check type for this event 
+    const cRecording *recording;     // or for this recording
+//out
+    tvType type;                	 //typeSeries or typeMovie
+    int movieId;
+    int seriesId;
+    int episodeId;
+};
+
+//Data structure for full series and episode information
+class cMovie {
+public:
+    cMovie(void) {
+        title = "";
+        originalTitle = "";
+        tagline = "";    
+        overview = "";
+        adult = false;
+        collectionName = "";
+        budget = 0;
+        revenue = 0;
+        genres = "";
+        homepage = "";
+        releaseDate = "";
+        runtime = 0;
+        popularity = 0.0;
+        voteAverage = 0.0;
+    };
+//IN
+    int movieId;                    // movieId fetched from ScraperGetEventType
+//OUT    
+    std::string title;
+    std::string originalTitle;
+    std::string tagline;    
+    std::string overview;
+    bool adult;
+    std::string collectionName;
+    int budget;
+    int revenue;
+    std::string genres;
+    std::string homepage;
+    std::string releaseDate;
+    int runtime;
+    float popularity;
+    float voteAverage;
+    cTvMedia poster;
+    cTvMedia fanart;
+    cTvMedia collectionPoster;
+    cTvMedia collectionFanart;
+    std::vector<cActor> actors;
+};
+
+//Data structure for full series and episode information
+class cSeries {
+public:
+    cSeries(void) {
+        seriesId = 0;
+        episodeId = 0;
+        name = "";
+        overview = "";
+        firstAired = "";
+        network = "";
+        genre = "";
+        rating = 0.0;
+        status = "";
+    };
+//IN
+    int seriesId;                   // seriesId fetched from ScraperGetEventType
+    int episodeId;                  // episodeId fetched from ScraperGetEventType
+//OUT
+    std::string name;
+    std::string overview;
+    std::string firstAired;
+    std::string network;
+    std::string genre;
+    float rating;
+    std::string status;
+    cEpisode episode;
+    std::vector<cActor> actors;
+    std::vector<cTvMedia> posters;
+    std::vector<cTvMedia> banners;
+    std::vector<cTvMedia> fanarts;
+    cTvMedia seasonPoster;
+};
+
+// Data structure for service "GetPosterBanner"
+class ScraperGetPosterBanner {
+public:
+	ScraperGetPosterBanner(void) {
+		type = tNone;
+	};
+// in
+    const cEvent *event;             // check type for this event 
+//out
+    tvType type;                	 //typeSeries or typeMovie
+    cTvMedia poster;
+    cTvMedia banner;
+};
+
+// Data structure for service "GetPoster"
+class ScraperGetPoster {
+public:
+// in
+    const cEvent *event;             // check type for this event
+    const cRecording *recording;     // or for this recording
+//out
+    cTvMedia poster;
+};
+
+// Data structure for service "GetPosterThumb"
+class ScraperGetPosterThumb {
+public:
+// in
+    const cEvent *event;             // check type for this event
+    const cRecording *recording;     // or for this recording
+//out
+    cTvMedia poster;
+};
+
+#endif //__SCRAPER2VDRSERVICES_H
\ No newline at end of file
diff --git a/udpreplier.c b/udpreplier.c
index a98a1e2..88f83e4 100644
--- a/udpreplier.c
+++ b/udpreplier.c
@@ -84,11 +84,11 @@ int UDPReplier::run(USHORT port, char* serverName, USHORT serverPort)
   USHORT temp = htons(serverPort);
   memcpy(&message[26], &temp, 2);
   
-  ULONG temp2 = htonl(VompClientRRProc::getProtocolVersion());
+  ULONG temp2 = htonl(VompClientRRProc::getProtocolVersionMin());
   memcpy(&message[28], &temp2, 4);
   
   strcpy(&message[32], serverName);
-
+  // Fix Me add also the maximum version somewhere
   if (!ds.init(port))
   {
     shutdown();
diff --git a/vdrcommand.h b/vdrcommand.h
index 76dde28..c9e4032 100644
--- a/vdrcommand.h
+++ b/vdrcommand.h
@@ -15,7 +15,7 @@
 
     You should have received a copy of the GNU General Public License
     along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
 
 #ifndef VDRCOMMAND_H
@@ -63,11 +63,22 @@ const static ULONG VDR_GETCHANNELPIDS      = 22;
 const static ULONG VDR_DELETETIMER         = 23;
 const static ULONG VDR_GETLANGUAGELIST     = 33;
 const static ULONG VDR_GETLANGUAGECONTENT  = 34;
+const static ULONG VDR_SETCHARSET          = 37;
 const static ULONG VDR_GETMEDIALIST        = 30;
 const static ULONG VDR_OPENMEDIA           = 31;
 const static ULONG VDR_GETMEDIABLOCK       = 32;
 const static ULONG VDR_GETMEDIAINFO        = 35;
 const static ULONG VDR_CLOSECHANNEL        = 36;
+const static ULONG VDR_GETRECSCRAPEREVENTTYPE = 38;
+const static ULONG VDR_GETSCRAPERMOVIEINFO = 39;
+const static ULONG VDR_GETSCRAPERSERIESINFO = 40;
+const static ULONG VDR_LOADTVMEDIA          =41;
+const static ULONG VDR_LOADTVMEDIARECTHUMB  =42;
+const static ULONG VDR_GETEVENTSCRAPEREVENTTYPE = 43;
+const static ULONG VDR_LOADTVMEDIAEVENTTHUMB  =44;
+const static ULONG VDR_LOADCHANNELLOGO = 45;
+
+const static ULONG VDR_SHUTDOWN            = 666;
 
 class VDR_Command : public SerializableList {
   public:
diff --git a/vomp.conf.sample b/vomp.conf.sample
index e83756a..49b6caf 100644
--- a/vomp.conf.sample
+++ b/vomp.conf.sample
@@ -34,3 +34,13 @@
 
 # MVPRelay enabled = yes
 
+# Change the following to the directory, where the channel logos reside,
+# all png and the channel name in lower case
+# if not set a logo directory below the plugin directory is used
+
+# Channel logo directory = /logodir
+
+# Change the following to the directory, where the EPG image reside,
+# if not set a image directory is try to be set automatically
+
+# Epg image directory = /EpgImage
diff --git a/vompclient.c b/vompclient.c
index 011b5c6..37de93c 100644
--- a/vompclient.c
+++ b/vompclient.c
@@ -29,23 +29,32 @@
 #ifndef VOMPSTANDALONE
 #include <vdr/channels.h>
 #include <vdr/recording.h>
+#include <vdr/plugin.h>
 #include "recplayer.h"
 #include "mvpreceiver.h"
+#include "picturereader.h"
 #endif
 
 
 
 pthread_mutex_t threadClientMutex;
 int VompClient::nr_clients = 0;
+cPlugin *VompClient::scraper = NULL;
+time_t  VompClient::lastScrapQuery = 0;
 
-
-VompClient::VompClient(Config* cfgBase, char* tconfigDir, int tsocket)
+VompClient::VompClient(Config* cfgBase, char* tconfigDir, char* tlogoDir, 
+	    char *tresourceDir, char * timageDir, char * tcacheDir, int tsocket)
  : rrproc(*this), tcp(tsocket), i18n(tconfigDir)
 {
 #ifndef VOMPSTANDALONE
   lp = NULL;
   recplayer = NULL;
-  recordingManager = NULL;
+  pict = new PictureReader(this);
+  if (!scraper) scrapQuery();
+  logoDir = tlogoDir;
+  resourceDir = tresourceDir;
+  imageDir = timageDir;
+  cacheDir = tcacheDir;
 #endif
   log = Log::getInstance();
   loggedIn = false;
@@ -79,14 +88,14 @@ VompClient::~VompClient()
     writeResumeData();
 
     delete recplayer;
-    delete recordingManager;
     recplayer = NULL;
-    recordingManager = NULL;
   }
 #endif
   //if (loggedIn) cleanConfig();
   decClients();
   
+  delete pict;
+  
   delete media;
   delete mediaprovider;
 
@@ -101,6 +110,16 @@ VompClient::~VompClient()
   }
 }
 
+cPlugin *VompClient::scrapQuery()
+{
+    if (scraper) return scraper;
+    if ((time(NULL)-lastScrapQuery) > 5*60) {
+	lastScrapQuery = time(NULL); 
+	  if (!scraper) scraper = cPluginManager::GetPlugin("scraper2vdr");
+   }
+   return scraper;
+}
+
 void VompClient::setCharset(int charset)
 {
    charcoding=charset;
@@ -237,6 +256,7 @@ void VompClient::run2()
 //  tcp.setSoKeepTime(3);
   tcp.setNonBlocking();
 
+  pict->init(&tcp);
   ULONG channelID;
   ULONG requestID;
   ULONG opcode;
@@ -386,38 +406,6 @@ ULLONG VompClient::htonll(ULLONG a)
 
 #ifndef VOMPSTANDALONE
 
-cChannel* VompClient::channelFromNumber(ULONG channelNumber)
-{
-  cChannel* channel = NULL;
-
-  for (channel = Channels.First(); channel; channel = Channels.Next(channel))
-  {
-    if (!channel->GroupSep())
-    {
-      log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
-
-      if (channel->Number() == (int)channelNumber)
-      {
-        int vpid = channel->Vpid();
-#if VDRVERSNUM < 10300
-        int apid1 = channel->Apid1();
-#else
-        int apid1 = channel->Apid(0);
-#endif
-        log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
-        return channel;
-      }
-    }
-  }
-
-  if (!channel)
-  {
-    log->log("Client", Log::DEBUG, "Channel not found");
-  }
-
-  return channel;
-}
-
 void VompClient::writeResumeData()
 {
   /*config.setValueLong("ResumeData",
diff --git a/vompclient.h b/vompclient.h
index 4ec905e..7a4f20c 100644
--- a/vompclient.h
+++ b/vompclient.h
@@ -48,7 +48,7 @@
 class RecPlayer;
 class MVPReceiver;
 class cChannel;
-class cRecordings;
+class cPlugin;
 #endif
 
 #include "defines.h"
@@ -62,13 +62,16 @@ class ResponsePacket;
 class ServerMediaFile;
 class SerializeBuffer;
 class MediaPlayer;
+class PictureReader;
 
 class VompClient
 {
   friend class VompClientRRProc;
+  friend class PictureReader;
 
   public:
-    VompClient(Config* baseConfig, char* configDir, int tsocket);
+    VompClient(Config* baseConfig, char* configDir, char* logoDir, 
+	char* resourceDir, char* imageDir, char*cacheDir,  int tsocket);
     ~VompClient();
 
     int run();
@@ -101,12 +104,19 @@ class VompClient
     //void cleanConfig();
 
 #ifndef VOMPSTANDALONE
-    cChannel* channelFromNumber(ULONG channelNumber);
     void writeResumeData();
 
     MVPReceiver* lp;
-    cRecordings* recordingManager;
     RecPlayer* recplayer;
+    static cPlugin * scraper;
+    static time_t lastScrapQuery;
+    static cPlugin*  scrapQuery();
+    PictureReader * pict;
+    char *logoDir;
+    char *imageDir;
+    char *resourceDir;
+    char *cacheDir;
+
 #endif
     MediaPlayer *media;
     ServerMediaFile *mediaprovider;
@@ -116,6 +126,7 @@ class VompClient
     cCharSetConv *charconvutf8;
     cCharSetConv *charconvsys;
     
+    
 };
 
 #endif
diff --git a/vompclientrrproc.c b/vompclientrrproc.c
index 841d382..f8e8d7d 100644
--- a/vompclientrrproc.c
+++ b/vompclientrrproc.c
@@ -30,6 +30,7 @@
 #include <vdr/remote.h>
 #include "recplayer.h"
 #include "mvpreceiver.h"
+#include "services/scraper2vdr.h"
 #endif
 
 #include "vompclientrrproc.h"
@@ -40,20 +41,65 @@
 #include "servermediafile.h"
 #include "i18n.h"
 #include "vdrcommand.h"
+#include "picturereader.h"
 
 bool ResumeIDLock;
 
-ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION = 0x00000300;
+ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MIN = 0x00000301;
+ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MAX = 0x00000400;
 // format is aabbccdd
 // cc is release protocol version, increase with every release, that changes protocol
 // dd is development protocol version, set to zero at every release, 
 // increase for every protocol change in git
 // bb not equal zero should indicate a non loggytronic protocol
 // aa is reserved for future use
+// VOMP_PROTOCOL_VERSION_MIN is the protocol version minimal supported by the server
+// VOMP_PROTOCOL_VERSION_MAX is the protocol version maximal supported by the server
+// This allows to run older clients from a new server
+// Increase the minimal protocol version everytime you break compatibility for a certain 
+// command. 
+
+
+/* Locking information from VDR:
+
+  + Instead of directly accessing the global variables Timers, Channels or Recordings,
+    they need to set up a cStateKey variable and call the proper getter function,
+    as in
+      cStateKey StateKey;
+      if (const cTimers *Timers = cTimers::GetTimersRead(StateKey)) {
+         // access the timers
+         StateKey.Remove();
+         }
+    and
+      cStateKey StateKey;
+      if (cTimers *Timers = cTimers::GetTimersWrite(StateKey)) {
+         // access the timers
+         StateKey.Remove();
+         }
+    See timers.h, thread.h and tools.h for details on this new locking mechanism.
+  + There are convenience macros for easily accessing these lists without having
+    to explicitly set up a cStateKey and calling its Remove() function. These macros
+    have the form LOCK_*_READ/WRITE (with '*' being TIMERS, CHANNELS, SCHEDULES or
+    RECORDINGS). Simply put such a macro before the point where you need to access
+    the respective list, and there will be a pointer named Timers, Channels, Schedules
+    or Recordings, respectively, which is valid until the end of the current block.
+  + If a plugin needs to access several of the global lists in parallel, locking must
+    always be done in the sequence Timers, Channels, Recordings, Schedules. This is
+    necessary to make sure that different threads that need to lock several lists at
+    the same time don't end up in a deadlock.
+
+    */
+
+// TODO: Use VDRs recording->ChangeName(option)) for move recording ?
+
+ULONG VompClientRRProc::getProtocolVersionMin()
+{
+  return VOMP_PROTOCOL_VERSION_MIN;
+}
 
-ULONG VompClientRRProc::getProtocolVersion()
+ULONG VompClientRRProc::getProtocolVersionMax()
 {
-  return VOMP_PROTOCOL_VERSION;
+  return VOMP_PROTOCOL_VERSION_MAX;
 }
 
 VompClientRRProc::VompClientRRProc(VompClient& x)
@@ -262,6 +308,30 @@ bool VompClientRRProc::processPacket()
     case 666:
       result = processVDRShutdown();
       break;
+    case VDR_GETRECSCRAPEREVENTTYPE:
+      result = processGetRecScraperEventType();
+    break;
+    case VDR_GETSCRAPERMOVIEINFO:
+      result = processGetScraperMovieInfo();
+    break;
+    case VDR_GETSCRAPERSERIESINFO:
+      result = processGetScraperSeriesInfo();
+    break;
+    case VDR_LOADTVMEDIA:
+      result = processLoadTvMedia();
+    break;
+    case VDR_LOADTVMEDIARECTHUMB:
+      result = processLoadTvMediaRecThumb();
+    break;
+    case VDR_GETEVENTSCRAPEREVENTTYPE:
+      result = processGetEventScraperEventType();
+    break;
+    case VDR_LOADTVMEDIAEVENTTHUMB:
+      result = processLoadTvMediaEventThumb();
+    break;
+    case VDR_LOADCHANNELLOGO:
+      result = processLoadChannelLogo();
+    break;
 #endif
     case VDR_GETMEDIALIST:
       result = processGetMediaList();
@@ -300,6 +370,7 @@ bool VompClientRRProc::processPacket()
   return false;
 }
 
+
 int VompClientRRProc::processLogin()
 {
   if (req->dataLength != 6) return 0;
@@ -318,7 +389,18 @@ int VompClientRRProc::processLogin()
 
   resp->addULONG(timeNow);
   resp->addLONG(timeOffset);
-  resp->addULONG(VOMP_PROTOCOL_VERSION);
+  resp->addULONG(VOMP_PROTOCOL_VERSION_MIN);
+  resp->addULONG(VOMP_PROTOCOL_VERSION_MAX);
+  
+  // also send information about languages
+  resp->addULONG(I18nLanguages()->Size());
+  resp->addLONG(Setup.DisplaySubtitles);
+  for (int i=0;i < I18nLanguages()->Size(); i++) {
+    resp->addLONG(Setup.AudioLanguages[i]);
+    resp->addLONG(Setup.SubtitleLanguages[i]);
+    resp->addString(I18nLanguageCode(i));
+  }
+  
   resp->finalise();
   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
   log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
@@ -668,10 +750,15 @@ int VompClientRRProc::processGetRecordingsList()
   resp->addULONG(FreeMB);
   resp->addULONG(Percent);
 
-  cRecordings Recordings;
-  Recordings.Load();
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_READ;
+  const cRecordings* tRecordings = Recordings;
+#else
+  cThreadLock RecordingsLock(&Recordings);
+  const cRecordings* tRecordings = &Recordings;
+#endif
 
-  for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
+  for (const cRecording *recording = tRecordings->First(); recording; recording = tRecordings->Next(recording))
   {
 #if VDRVERSNUM < 10721
     resp->addULONG(recording->start);
@@ -695,10 +782,15 @@ int VompClientRRProc::processDeleteRecording()
 {
   // data is a pointer to the fileName string
 
-  cRecordings Recordings;
-  Recordings.Load(); // probably have to do this
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_WRITE;
+  cRecordings* tRecordings = Recordings;
+#else
+  cThreadLock RecordingsLock(&Recordings);
+  cRecordings* tRecordings = &Recordings;
+#endif
 
-  cRecording* recording = Recordings.GetByName((char*)req->data);
+  cRecording* recording = tRecordings->GetByName((char*)req->data);
 
   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
 
@@ -706,13 +798,17 @@ int VompClientRRProc::processDeleteRecording()
   {
     log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
 
+// TODO: Switch to using: cRecording::IsInUse(void) const
     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
     if (!rc)
     {
       if (recording->Delete())
       {
+#if VDRVERSNUM >= 20301
+         tRecordings->DelByName(recording->FileName());
+         tRecordings->SetModified();
+#elif VDRVERSNUM > 10300
         // Copy svdrp's way of doing this, see if it works
-#if VDRVERSNUM > 10300
         ::Recordings.DelByName(recording->FileName());
 #endif
         resp->addULONG(1);
@@ -754,15 +850,22 @@ int VompClientRRProc::processMoveRecording()
   }
   if (!newPath) return 0;
 
-  cRecordings Recordings;
-  Recordings.Load(); // probably have to do this
 
-  cRecording* recording = Recordings.GetByName((char*)fileName);
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_WRITE;
+  cRecordings* tRecordings = Recordings;
+#else
+  cThreadLock RecordingsLock(&Recordings);
+  cRecordings* tRecordings = &Recordings;
+#endif
+
+  cRecording* recording = tRecordings->GetByName((char*)fileName);
 
   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
 
   if (recording)
   {
+    // TODO: Switch to using: int cRecording::IsInUse(void) const
     cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
     if (!rc)
     {
@@ -857,6 +960,7 @@ int VompClientRRProc::processMoveRecording()
         return 1;
       }
 
+
       // Ok, the directory container has been made, or it pre-existed.
 
       char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
@@ -873,12 +977,10 @@ int VompClientRRProc::processMoveRecording()
         log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
         rmdir(oldTitleDir); // can't do anything about a fail result at this point.
         delete[] oldTitleDir;
-      }
 
-      if (renameret == 0)
-      {
-#if VDRVERSNUM > 10311
-        // Tell VDR
+#if VDRVERSNUM >= 20301
+        tRecordings->SetModified();
+#elif VDRVERSNUM > 10311
         ::Recordings.Update();
 #endif
         // Success. Send a different packet from just a ulong
@@ -923,7 +1025,14 @@ int VompClientRRProc::processGetChannelsList()
   int allChans = 1;
   if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
 
-  for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  const cChannels* tChannels = Channels;
+#else
+  const cChannels* tChannels = &Channels;
+#endif
+
+  for (const cChannel *channel = tChannels->First(); channel; channel = tChannels->Next(channel))
   {
 #if VDRVERSNUM < 10300
     if (!channel->GroupSep() && (!channel->Ca() || allChans))
@@ -964,7 +1073,14 @@ int VompClientRRProc::processGetChannelPids()
 {
   ULONG channelNumber = ntohl(*(ULONG*)req->data);
 
-  cChannel* channel = x.channelFromNumber(channelNumber);
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  const cChannels* tChannels = Channels;
+#else
+  cChannels* tChannels = &Channels;
+#endif
+
+  const cChannel* channel = tChannels->GetByNumber(channelNumber);
   if (!channel)
   {
     resp->addULONG(0);
@@ -1139,7 +1255,14 @@ int VompClientRRProc::processStartStreamingChannel()
   log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
   ULONG channelNumber = ntohl(*(ULONG*)req->data);
 
-  cChannel* channel = x.channelFromNumber(channelNumber);
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  const cChannels* tChannels = Channels;
+#else
+  cChannels* tChannels = &Channels;
+#endif
+
+  const cChannel* channel = tChannels->GetByNumber(channelNumber);
   if (!channel)
   {
     resp->addULONG(0);
@@ -1210,9 +1333,7 @@ int VompClientRRProc::processStopStreaming()
     x.writeResumeData();
 
     delete x.recplayer;
-    delete x.recordingManager;
     x.recplayer = NULL;
-    x.recordingManager = NULL;
   }
 
   resp->addULONG(1);
@@ -1267,10 +1388,15 @@ int VompClientRRProc::processStartStreamingRecording()
 {
   // data is a pointer to the fileName string
 
-  x.recordingManager = new cRecordings;
-  x.recordingManager->Load();
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_READ;
+  const cRecordings* tRecordings = Recordings;
+#else
+  cThreadLock RecordingsLock(&Recordings);
+  cRecordings* tRecordings = &Recordings;
+#endif
 
-  cRecording* recording = x.recordingManager->GetByName((char*)req->data);
+  const cRecording* recording = tRecordings->GetByName((char*)req->data);
 
   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
 
@@ -1292,11 +1418,7 @@ int VompClientRRProc::processStartStreamingRecording()
     
     log->log("RRProc", Log::DEBUG, "written totalLength");
   }
-  else
-  {
-    delete x.recordingManager;
-    x.recordingManager = NULL;
-  }
+
   return 1;
 }
 
@@ -1401,7 +1523,14 @@ int VompClientRRProc::processGetChannelSchedule()
 
   log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
 
-  cChannel* channel = x.channelFromNumber(channelNumber);
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  const cChannels* tChannels = Channels;
+#else
+  cChannels* tChannels = &Channels;
+#endif
+
+  const cChannel* channel = tChannels->GetByNumber(channelNumber);
   if (!channel)
   {
     resp->addULONG(0);
@@ -1416,12 +1545,16 @@ int VompClientRRProc::processGetChannelSchedule()
 
 #if VDRVERSNUM < 10300
   cMutexLock MutexLock;
-  const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
-#else
+  const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
+#elif VDRVERSNUM < 20301
   cSchedulesLock MutexLock;
-  const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
+  const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
+#else
+  LOCK_SCHEDULES_READ;
+  const cSchedules *tSchedules = Schedules;
 #endif
-  if (!Schedules)
+
+  if (!tSchedules)
   {
     resp->addULONG(0);
     resp->finalise();
@@ -1433,7 +1566,7 @@ int VompClientRRProc::processGetChannelSchedule()
 
   log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
 
-  const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
+  const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
   if (!Schedule)
   {
     resp->addULONG(0);
@@ -1525,14 +1658,21 @@ int VompClientRRProc::processGetChannelSchedule()
 
 int VompClientRRProc::processGetTimers()
 {
-  cTimer *timer;
-  int numTimers = Timers.Count();
+#if VDRVERSNUM >= 20301
+  LOCK_TIMERS_READ;
+  const cTimers* tTimers = Timers;
+#else
+  const cTimers* tTimers = &Timers;
+#endif
+
+  const cTimer *timer;
+  int numTimers = tTimers->Count();
 
   resp->addULONG(numTimers);
 
   for (int i = 0; i < numTimers; i++)
   {
-    timer = Timers.Get(i);
+    timer = tTimers->Get(i);
 
 #if VDRVERSNUM < 10300
     resp->addULONG(timer->Active());
@@ -1597,36 +1737,43 @@ int VompClientRRProc::processSetTimer()
   log->log("RRProc", Log::DEBUG, "%s", timerString);
 
   cTimer *timer = new cTimer;
-  if (timer->Parse((char*)timerString))
+  if (!timer->Parse((char*)timerString))
   {
-    cTimer *t = Timers.GetTimer(timer);
-    if (!t)
-    {
-      Timers.Add(timer);
-#if VDRVERSNUM < 10300
-      Timers.Save();
+    resp->addULONG(2);
+    resp->finalise();
+    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+    delete timer;
+    return 1;
+  }
+
+#if VDRVERSNUM >= 20301
+  LOCK_TIMERS_WRITE;
+  cTimers* tTimers = Timers;
 #else
-      Timers.SetModified();
+  cTimers* tTimers = &Timers;
 #endif
-      resp->addULONG(0);
-      resp->finalise();
-      x.tcp.sendPacket(resp->getPtr(), resp->getLen());
-      return 1; // FIXME - cTimer* timer is leaked here!
-    }
-    else
-    {
-      resp->addULONG(1);
-      resp->finalise();
-      x.tcp.sendPacket(resp->getPtr(), resp->getLen());
-    }
-  }
-  else
+
+  cTimer *t = tTimers->GetTimer(timer);
+  if (t)
   {
-    resp->addULONG(2);
+    resp->addULONG(1);
     resp->finalise();
     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+    delete timer;
+    return 1;
   }
-  delete timer;
+
+  timer->ClrFlags(tfRecording);
+  tTimers->Add(timer);
+#if VDRVERSNUM < 10300
+  tTimers->Save();
+#elif VDRVERSNUM < 20301
+  tTimers->SetModified();
+#endif
+
+  resp->addULONG(0);
+  resp->finalise();
+  x.tcp.sendPacket(resp->getPtr(), resp->getLen());
   return 1;
 }
 
@@ -1642,9 +1789,16 @@ int VompClientRRProc::processDeleteTimer()
   INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;  
   INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;  
   INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
-    
+
+#if VDRVERSNUM >= 20301
+  LOCK_TIMERS_WRITE;
+  cTimers* tTimers = Timers;
+#else
+  cTimers* tTimers = &Timers;
+#endif
+
   cTimer* ti = NULL;
-  for (ti = Timers.First(); ti; ti = Timers.Next(ti))
+  for (ti = tTimers->First(); ti; ti = tTimers->Next(ti))
   {
     if  ( (ti->Channel()->Number() == delChannel)
      &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
@@ -1660,51 +1814,56 @@ int VompClientRRProc::processDeleteTimer()
     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
     return 1;
   }
-          
-  if (!Timers.BeingEdited())
-  {
-    if (!ti->Recording())
-    {
-      Timers.Del(ti);
-      Timers.SetModified();
-      resp->addULONG(10);
-      resp->finalise();
-      x.tcp.sendPacket(resp->getPtr(), resp->getLen());
-      return 1;
-    }
-    else
-    {
-      log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
-      resp->addULONG(3);
-      resp->finalise();
-      x.tcp.sendPacket(resp->getPtr(), resp->getLen());
-      return 1;
-    }  
-  }
-  else
+
+#if VDRVERSNUM < 20301
+// I suppose with the new locking this just can't happen
+  if (tTimers->BeingEdited())
   {
     log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
     resp->addULONG(1);
     resp->finalise();
     x.tcp.sendPacket(resp->getPtr(), resp->getLen());
     return 1;
-  }  
+  }
+#endif
+
+  if (ti->Recording())
+  {
+    log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
+    resp->addULONG(3);
+    resp->finalise();
+    x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+    return 1;
+  }
+
+  tTimers->Del(ti);
+  tTimers->SetModified();
+
+  resp->addULONG(10);
+  resp->finalise();
+  x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+  return 1;
 }
 
 int VompClientRRProc::processGetRecInfo()
 {
   // data is a pointer to the fileName string
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_READ;
+  const cRecordings* tRecordings = Recordings;
+#else
+  cThreadLock RecordingsLock(&Recordings);
+  cRecordings* tRecordings = &Recordings;
+#endif
 
-  cRecordings Recordings;
-  Recordings.Load(); // probably have to do this
-
-  cRecording *recording = Recordings.GetByName((char*)req->data);
+  const cRecording *recording = tRecordings->GetByName((char*)req->data);
 
   time_t timerStart = 0;
   time_t timerStop = 0;
   char* summary = NULL;
   char* shorttext = NULL;
   char* description = NULL;
+  char* title = NULL;
   bool newsummary=false;
   ULONG resumePoint = 0;
 
@@ -1872,7 +2031,16 @@ int VompClientRRProc::processGetRecInfo()
   framespersec = Info->FramesPerSecond();
 #endif
   resp->adddouble(framespersec);
-
+  title = (char*)Info->Title();
+  if (title) 
+  {
+    resp->addString(x.charconvsys->Convert(title));
+  }
+  else
+  {
+      resp->addString(x.charconvsys->Convert(recording->Name()));
+  }
+  
   // Done. send it
 
   resp->finalise();
@@ -1911,17 +2079,20 @@ int VompClientRRProc::processReScanRecording()
 int VompClientRRProc::processGetMarks()
 {
   // data is a pointer to the fileName string
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_READ;
+  const cRecordings* tRecordings = Recordings;
+#else
+  cThreadLock RecordingsLock(&Recordings);
+  cRecordings* tRecordings = &Recordings;
+#endif
 
-  cMarks Marks;
-  cRecordings Recordings;
-  Recordings.Load(); // probably have to do this
-
-  cRecording *recording = Recordings.GetByName((char*)req->data);
-
+  const cRecording *recording = tRecordings->GetByName((char*)req->data);
   log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
 
   if (recording)
   {
+    cMarks Marks;
 #if VDRVERSNUM < 10703
     Marks.Load(recording->FileName());
 #else
@@ -1966,7 +2137,336 @@ int VompClientRRProc::processVDRShutdown()
   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
   return 1;
 }
+
+int VompClientRRProc::processGetRecScraperEventType()
+{
+#if VDRVERSNUM >= 20301
+  LOCK_RECORDINGS_READ;
+  const cRecordings* tRecordings = Recordings;
+#else
+  cThreadLock RecordingsLock(&Recordings);
+  cRecordings* tRecordings = &Recordings;
+#endif
+
+  const cRecording *recording = tRecordings->GetByName((char*)req->data);
+  ScraperGetEventType call;
+  call.type = tNone;
+
+  if (recording && x.scrapQuery()) 
+  {
+     call.recording = recording;
+     x.scraper->Service("GetEventType", &call);
+  }
+  resp->addUCHAR(call.type);
+  if (call.type == tMovie)
+  {
+     resp->addLONG(call.movieId);
+  }
+  else if (call.type == tSeries)
+  {
+     resp->addLONG(call.seriesId);
+     resp->addLONG(call.episodeId);
+  }
+  resp->finalise();
+  x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+
+  return 1;
+}
+
+int VompClientRRProc::processGetEventScraperEventType()
+{
+  ScraperGetEventType call;
+  call.type = tNone;
+  ULONG channelid = ntohl(*(ULONG*)req->data);
+  ULONG eventid = ntohl(*(ULONG*)(req->data+4));
+  const cEvent *event = NULL; 
+
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  const cChannels* tChannels = Channels;
+#else
+  cChannels* tChannels = &Channels;
+#endif
+
+  const cChannel* channel = tChannels->GetByNumber(channelid);
+
+#if VDRVERSNUM < 10300
+  cMutexLock MutexLock;
+  const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
+#elif VDRVERSNUM < 20301
+  cSchedulesLock MutexLock;
+  const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
+#else
+  LOCK_SCHEDULES_READ;
+  const cSchedules *tSchedules = Schedules;
+#endif
+
+  if (tSchedules && channel)
+  {
+     const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
+     if (Schedule) {
+        event = Schedule->GetEvent(eventid);
+    }
+  }
+    
+  if (event && x.scrapQuery()) 
+  {
+     call.event = event;
+     x.scraper->Service("GetEventType",&call);
+  }
+  resp->addUCHAR(call.type);
+  if (call.type == tMovie)
+  {
+     resp->addLONG(call.movieId);
+  } else if (call.type == tSeries){
+     resp->addLONG(call.seriesId);
+     resp->addLONG(call.episodeId);
+  }
+  if (x.pict->epgImageExists(eventid)) {
+     resp->addLONG(1);
+  } else {
+     resp->addLONG(0);
+  }
+    
+  resp->finalise();
+  x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+
+  return 1;
+}
+
+#define ADDSTRING_TO_PAKET(y) if ((y)!=0)  resp->addString(x.charconvutf8->Convert(y)); else resp->addString(""); 
+
+int VompClientRRProc::processGetScraperMovieInfo()
+{
+   
+   cMovie movie;
+   movie.movieId = ntohl(*(ULONG*)req->data);
+   if (!x.scrapQuery()) {
+      log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
+      return 0; //stupid, I have no scraper why are you still asking
+   }
+   x.scraper->Service("GetMovie",&movie);
+   
+
+   ADDSTRING_TO_PAKET(movie.title.c_str());
+   ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
+   ADDSTRING_TO_PAKET(movie.tagline.c_str());
+   ADDSTRING_TO_PAKET(movie.overview.c_str());
+   resp->addUCHAR(movie.adult);
+   ADDSTRING_TO_PAKET(movie.collectionName.c_str());
+
+   resp->addLONG(movie.budget);
+   resp->addLONG(movie.revenue);
+   ADDSTRING_TO_PAKET(movie.genres.c_str());
+   ADDSTRING_TO_PAKET(movie.homepage.c_str());
+   ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
+   resp->addLONG(movie.runtime);
+   resp->adddouble(movie.popularity);
+   resp->adddouble(movie.voteAverage);
+   resp->addULONG(movie.poster.width);
+   resp->addULONG(movie.poster.height);
+   resp->addULONG(movie.fanart.width);
+   resp->addULONG(movie.fanart.height);
+   resp->addULONG(movie.collectionPoster.width);
+   resp->addULONG(movie.collectionPoster.height);
+   resp->addULONG(movie.collectionFanart.width);
+   resp->addULONG(movie.collectionFanart.height);
+   resp->addULONG(movie.actors.size());
+   for (ULONG acty=0; acty < movie.actors.size(); acty++) {
+       ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
+       ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
+       resp->addULONG(movie.actors[acty].actorThumb.width);
+       resp->addULONG(movie.actors[acty].actorThumb.height);
+   }
+  resp->finalise();
   
-#endif // !VOMPSTANDALONE
+  x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+
+  
+  return 1;
+
+}
+
+int VompClientRRProc::processGetScraperSeriesInfo()
+{
+   cSeries series;
+   series.seriesId = ntohl(*(ULONG*)req->data);
+   series.episodeId = ntohl(*(ULONG*)(req->data+4));
+   if (!x.scrapQuery()) {
+      log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
+      return 0; //stupid, I have no scraper why are you still asking
+   }
+   x.scraper->Service("GetSeries",&series);
+   
+   ADDSTRING_TO_PAKET(series.name.c_str());
+   ADDSTRING_TO_PAKET(series.overview.c_str());
+   ADDSTRING_TO_PAKET(series.firstAired.c_str());
+   ADDSTRING_TO_PAKET(series.network.c_str());
+   ADDSTRING_TO_PAKET(series.genre.c_str());
+   resp->adddouble(series.rating);
+   ADDSTRING_TO_PAKET(series.status.c_str());
+   
+   resp->addLONG(series.episode.number);
+   resp->addLONG(series.episode.season);
+   ADDSTRING_TO_PAKET(series.episode.name.c_str());
+   ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
+   ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
+   ADDSTRING_TO_PAKET(series.episode.overview.c_str());
+   resp->adddouble(series.episode.rating);
+   resp->addULONG(series.episode.episodeImage.width);
+   resp->addULONG(series.episode.episodeImage.height);
+   
+   ULONG num_actors = series.actors.size();
+   resp->addULONG(num_actors);
+   for (ULONG acty=0; acty < num_actors; acty++) {
+       ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
+       ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
+       resp->addULONG(series.actors[acty].actorThumb.width);
+       resp->addULONG(series.actors[acty].actorThumb.height);
+   }
+   ULONG num_posters = series.posters.size();
+   resp->addULONG(num_posters);
+   for (ULONG medias = 0; medias < num_posters; medias++ ) {
+       cTvMedia media=series.posters[medias];
+       resp->addULONG(media.width);
+       resp->addULONG(media.height);
+   }
+
+   ULONG num_banners = series.banners.size();
+   resp->addULONG(num_banners);
+   for (ULONG medias = 0; medias < num_banners; medias++ ) {
+       cTvMedia media=series.banners[medias];
+       resp->addULONG(media.width);
+       resp->addULONG(media.height);
+   }
+   ULONG num_fanarts = series.fanarts.size();
+   resp->addULONG(num_fanarts);
+   for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
+       cTvMedia media=series.fanarts[medias];
+       resp->addULONG(media.width);
+       resp->addULONG(media.height);
+   }
+   resp->addULONG(series.seasonPoster.width);
+   resp->addULONG(series.seasonPoster.height);
+   
+   resp->finalise();
+
+   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+   
+   return 1;
+}
+
+int VompClientRRProc::processLoadTvMedia()
+{
+   TVMediaRequest tvreq;
+   tvreq.streamID = req->requestID;
+   tvreq.type = ntohl(*(ULONG*)req->data);
+   tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
+   tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
+   tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
+   tvreq.container = ntohl(*(ULONG*)(req->data+16));
+   tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
+   log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
+   x.pict->addTVMediaRequest(tvreq);
+
+   
+   resp->finalise();
+
+   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+   
+   return 1;
+}
 
+int VompClientRRProc::processLoadTvMediaRecThumb()
+{
+   TVMediaRequest tvreq;
+   tvreq.streamID = req->requestID;
+   tvreq.type = 3; // unknown but primary_name is set
+   tvreq.primary_id = 0;
+   tvreq.primary_name = std::string((const char*) req->data);
+   tvreq.secondary_id = 0;
+   tvreq.type_pict = 1;
+   tvreq.container = 0;
+   tvreq.container_member = 0;
+   log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
+   x.pict->addTVMediaRequest(tvreq);
+
+   
+   resp->finalise();
+
+   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+   
+   return 1;
+}
+
+int VompClientRRProc::processLoadTvMediaEventThumb()
+{
+   TVMediaRequest tvreq;
+   tvreq.streamID = req->requestID;
+   tvreq.type = 4; // unknown but primary_id is set
+   UINT channelid = ntohl(*(ULONG*)req->data);
+   tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
+   tvreq.secondary_id = 0;
+
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  const cChannels* tChannels = Channels;
+#else
+  cChannels* tChannels = &Channels;
+#endif
+
+   const cChannel* channel = tChannels->GetByNumber(channelid);
+
+   if (channel) tvreq.primary_name = std::string((const char*)channel->GetChannelID().ToString());
+   tvreq.type_pict = 1;
+   tvreq.container = 0;
+   tvreq.container_member = 0;
+   log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
+   x.pict->addTVMediaRequest(tvreq);
+
+   
+   resp->finalise();
+
+   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+   
+   return 1;
+}
+
+int VompClientRRProc::processLoadChannelLogo()
+{
+   TVMediaRequest tvreq;
+   tvreq.streamID = req->requestID;
+   tvreq.type = 5; // channel logo
+   UINT channelid = ntohl(*(ULONG*)req->data);
+   tvreq.primary_id = channelid;
+   tvreq.secondary_id = 0;
+
+#if VDRVERSNUM >= 20301
+  LOCK_CHANNELS_READ;
+  const cChannels* tChannels = Channels;
+#else
+  cChannels* tChannels = &Channels;
+#endif
+
+   const cChannel* channel = tChannels->GetByNumber(channelid);
+
+   if (channel) tvreq.primary_name = std::string((const char*)channel->Name());
+   tvreq.type_pict = 1;
+   tvreq.container = 0;
+   tvreq.container_member = 0;
+   if (channel) log->log("RRProc", Log::DEBUG, "TVMedia request %d %d %s",req->requestID,channelid, channel->Name());
+   x.pict->addTVMediaRequest(tvreq);
+
+   
+   resp->finalise();
+
+   x.tcp.sendPacket(resp->getPtr(), resp->getLen());
+   
+   return 1;
+}
+
+
+
+  
+#endif // !VOMPSTANDALONE
 
diff --git a/vompclientrrproc.h b/vompclientrrproc.h
index da6ef12..1c0fb6b 100644
--- a/vompclientrrproc.h
+++ b/vompclientrrproc.h
@@ -52,7 +52,8 @@ class VompClientRRProc : public Thread
   public:
     VompClientRRProc(VompClient& x);
     ~VompClientRRProc();
-    static ULONG getProtocolVersion();
+    static ULONG getProtocolVersionMin();
+    static ULONG getProtocolVersionMax();
     
     bool init();
     bool recvRequest(RequestPacket*);
@@ -82,6 +83,15 @@ class VompClientRRProc : public Thread
     int processDeleteTimer();
     int processReScanRecording();           // FIXME obselete
     int processVDRShutdown();
+    int processGetRecScraperEventType();
+    int processGetScraperMovieInfo();
+    int processGetScraperSeriesInfo();
+    int processLoadTvMedia();
+    int processLoadTvMediaRecThumb();
+    int processGetEventScraperEventType();
+    int processLoadTvMediaEventThumb();
+    int processLoadChannelLogo();
+
 #endif
     int processLogin();
     int processConfigSave();
@@ -101,7 +111,8 @@ class VompClientRRProc : public Thread
     RequestPacket* req;
     RequestPacketQueue req_queue;
     ResponsePacket* resp;
-    static ULONG VOMP_PROTOCOL_VERSION;
+    static ULONG VOMP_PROTOCOL_VERSION_MIN;
+    static ULONG VOMP_PROTOCOL_VERSION_MAX;
     
     Log* log;
 };
diff --git a/vompserver.c b/vompserver.c
index b59a037..6bc6e54 100644
--- a/vompserver.c
+++ b/vompserver.c
@@ -27,7 +27,7 @@
 #include "mvpserver.h"
 //#include "vompclient.h"
 
-static const char *VERSION        = "0.4.1";
+static const char *VERSION        = "0.5.0";
 static const char *DESCRIPTION    = "Vompserver plugin by Chris Tallon";
 static const char *MAINMENUENTRY  = "Vompserver";
 
@@ -74,6 +74,7 @@ cPluginVompserver::~cPluginVompserver()
   // Clean up after yourself!
   mvpserver.stop();
   if (configDir) delete[] configDir;  
+
 }
 
 const char *cPluginVompserver::CommandLineHelp(void)
@@ -91,15 +92,15 @@ bool cPluginVompserver::ProcessArgs(int argc, char *argv[])
   {
     if (c == 'c')
     {
-      const char* vdrdeveldevelret = cPlugin::ConfigDirectory(optarg);
-      if (!vdrdeveldevelret)
+      const char* vdrret = cPlugin::ConfigDirectory(optarg);
+      if (!vdrret)
       {
         dsyslog("VOMP: Could not get config dir from VDR");
         return false;
       }
       
-      configDir = new char[strlen(vdrdeveldevelret)+1];
-      strcpy(configDir, vdrdeveldevelret);
+      configDir = new char[strlen(vdrret)+1];
+      strcpy(configDir, vdrret);
     }
     else
     {
@@ -122,14 +123,14 @@ bool cPluginVompserver::Start(void)
   
   if (!configDir)
   {
-    const char* vdrdeveldevelret = cPlugin::ConfigDirectory("vompserver");
-    if (!vdrdeveldevelret)
+    const char* vdrret = cPlugin::ConfigDirectory("vompserver");
+    if (!vdrret)
     {
       dsyslog("VOMP: Could not get config dir from VDR");
       return false;
     }
-    configDir = new char[strlen(vdrdeveldevelret)+1];
-    strcpy(configDir, vdrdeveldevelret);
+    configDir = new char[strlen(vdrret)+1];
+    strcpy(configDir, vdrret);
   }
   
   int success = mvpserver.run(configDir);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-vdr-dvb/vdr-plugin-vompserver.git



More information about the pkg-vdr-dvb-changes mailing list