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
--- /dev/null
+#include "picturereader.h"
+#include <vdr/plugin.h>
+#include <sstream>
+
+
+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);
+}
+
+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 != req.primary_id ||
+ series.episodeId != 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 != 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;
+
+ default:
+ return std::string("");
+ break;
+ };
+
+
+}
+
+
+void PictureReader::threadMethod()
+{
+ ULONG *p;
+ ULONG headerLength = sizeof(ULONG) * 4;
+ UCHAR buffer[headerLength];
+ int amountReceived;
+
+// 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");
+
+}
+
--- /dev/null
+/*
+ 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>
+
+struct TVMediaRequest{
+ ULONG streamID;
+ ULONG type;
+ ULONG primary_id;
+ ULONG secondary_id;
+ ULONG type_pict;
+ ULONG container;
+ ULONG container_member;
+};
+
+class PictureReader : public Thread
+{
+ public:
+ PictureReader(VompClient * client);
+ virtual ~PictureReader();
+ int init(TCP* tcp);
+ void detachMVPReceiver();
+ void addTVMediaRequest(TVMediaRequest&);
+
+ 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
+
+
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();
#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;
VompClient::VompClient(Config* cfgBase, char* tconfigDir, int tsocket)
: rrproc(*this), tcp(tsocket), i18n(tconfigDir)
lp = NULL;
recplayer = NULL;
recordingManager = NULL;
+ if (!scraper) scraper = cPluginManager::GetPlugin("scraper2vdr");
+ pict = new PictureReader(this);
#endif
log = Log::getInstance();
loggedIn = false;
//if (loggedIn) cleanConfig();
decClients();
+ delete pict;
+
delete media;
delete mediaprovider;
// tcp.setSoKeepTime(3);
tcp.setNonBlocking();
+ pict->init(&tcp);
ULONG channelID;
ULONG requestID;
ULONG opcode;
class MVPReceiver;
class cChannel;
class cRecordings;
+class cPlugin;
#endif
#include "defines.h"
class ServerMediaFile;
class SerializeBuffer;
class MediaPlayer;
+class PictureReader;
class VompClient
{
friend class VompClientRRProc;
+ friend class PictureReader;
public:
VompClient(Config* baseConfig, char* configDir, int tsocket);
MVPReceiver* lp;
cRecordings* recordingManager;
RecPlayer* recplayer;
+ static cPlugin * scraper;
+ PictureReader * pict;
+
#endif
MediaPlayer *media;
ServerMediaFile *mediaprovider;
cCharSetConv *charconvutf8;
cCharSetConv *charconvsys;
+
};
#endif
#include <vdr/remote.h>
#include "recplayer.h"
#include "mvpreceiver.h"
+#include "services/scraper2vdr.h"
#endif
#include "vompclientrrproc.h"
#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 = 0x00000302;
// 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.
-ULONG VompClientRRProc::getProtocolVersion()
+ULONG VompClientRRProc::getProtocolVersionMin()
{
- return VOMP_PROTOCOL_VERSION;
+ return VOMP_PROTOCOL_VERSION_MIN;
+}
+
+ULONG VompClientRRProc::getProtocolVersionMax()
+{
+ return VOMP_PROTOCOL_VERSION_MAX;
}
VompClientRRProc::VompClientRRProc(VompClient& x)
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;
#endif
case VDR_GETMEDIALIST:
result = processGetMediaList();
resp->addULONG(timeNow);
resp->addLONG(timeOffset);
- resp->addULONG(VOMP_PROTOCOL_VERSION);
+ resp->addULONG(VOMP_PROTOCOL_VERSION_MIN);
+ resp->addULONG(VOMP_PROTOCOL_VERSION_MAX);
resp->finalise();
x.tcp.sendPacket(resp->getPtr(), resp->getLen());
log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
resp->addULONG(Total);
resp->addULONG(FreeMB);
resp->addULONG(Percent);
-
+
cRecordings Recordings;
Recordings.Load();
int VompClientRRProc::processGetRecInfo()
{
// data is a pointer to the fileName string
-
+
cRecordings Recordings;
Recordings.Load(); // probably have to do this
char* summary = NULL;
char* shorttext = NULL;
char* description = NULL;
+ char* title = NULL;
bool newsummary=false;
ULONG resumePoint = 0;
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();
x.tcp.sendPacket(resp->getPtr(), resp->getLen());
return 1;
}
+
+int VompClientRRProc::processGetRecScraperEventType()
+{
+ Recordings.Load(); // probably have to do this
+
+ cRecording *recording = Recordings.GetByName((char*)req->data);
+ ScraperGetEventType call;
+ call.type = tNone;
+
+ if (recording && x.scraper)
+ {
+ 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;
+}
+
+#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.scraper) {
+ 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.scraper) {
+ 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;
+}
+
+
+
+
+#endif // !VOMPSTANDALONE
public:
VompClientRRProc(VompClient& x);
~VompClientRRProc();
- static ULONG getProtocolVersion();
+ static ULONG getProtocolVersionMin();
+ static ULONG getProtocolVersionMax();
bool init();
bool recvRequest(RequestPacket*);
int processDeleteTimer();
int processReScanRecording(); // FIXME obselete
int processVDRShutdown();
+ int processGetRecScraperEventType();
+ int processGetScraperMovieInfo();
+ int processGetScraperSeriesInfo();
+ int processLoadTvMedia();
#endif
int processLogin();
int processConfigSave();
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;
};