From 861bde0e99e8c34e90d2a8c2c236c66eb49fe55e Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Sun, 13 Jul 2014 15:38:35 +0200 Subject: [PATCH] Added first support for tvscraper, until now only recordings, introduced picture reader thread --- Makefile | 3 +- picturereader.c | 247 +++++++++++++++++++++++++++++++++++++++++++++ picturereader.h | 72 +++++++++++++ udpreplier.c | 4 +- vompclient.c | 9 +- vompclient.h | 7 ++ vompclientrrproc.c | 224 ++++++++++++++++++++++++++++++++++++++-- vompclientrrproc.h | 10 +- 8 files changed, 562 insertions(+), 14 deletions(-) create mode 100644 picturereader.c create mode 100644 picturereader.h diff --git a/Makefile b/Makefile index 9c99e28..528655a 100644 --- a/Makefile +++ b/Makefile @@ -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/picturereader.c b/picturereader.c new file mode 100644 index 0000000..62e4218 --- /dev/null +++ b/picturereader.c @@ -0,0 +1,247 @@ +#include "picturereader.h" +#include +#include + + +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_" <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"); + +} + diff --git a/picturereader.h b/picturereader.h new file mode 100644 index 0000000..195feeb --- /dev/null +++ b/picturereader.h @@ -0,0 +1,72 @@ +/* + 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 + +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 pictures; + + TCP* tcp; + VompClient * x; + cSeries series; + cMovie movie; + + protected: + void threadMethod(); +}; + +#endif + + 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/vompclient.c b/vompclient.c index 011b5c6..fb36de7 100644 --- a/vompclient.c +++ b/vompclient.c @@ -29,15 +29,17 @@ #ifndef VOMPSTANDALONE #include #include +#include #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) @@ -46,6 +48,8 @@ VompClient::VompClient(Config* cfgBase, char* tconfigDir, int tsocket) lp = NULL; recplayer = NULL; recordingManager = NULL; + if (!scraper) scraper = cPluginManager::GetPlugin("scraper2vdr"); + pict = new PictureReader(this); #endif log = Log::getInstance(); loggedIn = false; @@ -87,6 +91,8 @@ VompClient::~VompClient() //if (loggedIn) cleanConfig(); decClients(); + delete pict; + delete media; delete mediaprovider; @@ -237,6 +243,7 @@ void VompClient::run2() // tcp.setSoKeepTime(3); tcp.setNonBlocking(); + pict->init(&tcp); ULONG channelID; ULONG requestID; ULONG opcode; diff --git a/vompclient.h b/vompclient.h index 4ec905e..8c54b84 100644 --- a/vompclient.h +++ b/vompclient.h @@ -49,6 +49,7 @@ class RecPlayer; class MVPReceiver; class cChannel; class cRecordings; +class cPlugin; #endif #include "defines.h" @@ -62,10 +63,12 @@ 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); @@ -107,6 +110,9 @@ class VompClient MVPReceiver* lp; cRecordings* recordingManager; RecPlayer* recplayer; + static cPlugin * scraper; + PictureReader * pict; + #endif MediaPlayer *media; ServerMediaFile *mediaprovider; @@ -116,6 +122,7 @@ class VompClient cCharSetConv *charconvutf8; cCharSetConv *charconvsys; + }; #endif diff --git a/vompclientrrproc.c b/vompclientrrproc.c index 841d382..5a67249 100644 --- a/vompclientrrproc.c +++ b/vompclientrrproc.c @@ -30,6 +30,7 @@ #include #include "recplayer.h" #include "mvpreceiver.h" +#include "services/scraper2vdr.h" #endif #include "vompclientrrproc.h" @@ -40,20 +41,32 @@ #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) @@ -262,6 +275,18 @@ 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; #endif case VDR_GETMEDIALIST: result = processGetMediaList(); @@ -318,7 +343,8 @@ 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); resp->finalise(); x.tcp.sendPacket(resp->getPtr(), resp->getLen()); log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen()); @@ -667,7 +693,7 @@ int VompClientRRProc::processGetRecordingsList() resp->addULONG(Total); resp->addULONG(FreeMB); resp->addULONG(Percent); - + cRecordings Recordings; Recordings.Load(); @@ -1694,7 +1720,7 @@ int VompClientRRProc::processDeleteTimer() int VompClientRRProc::processGetRecInfo() { // data is a pointer to the fileName string - + cRecordings Recordings; Recordings.Load(); // probably have to do this @@ -1705,6 +1731,7 @@ int VompClientRRProc::processGetRecInfo() char* summary = NULL; char* shorttext = NULL; char* description = NULL; + char* title = NULL; bool newsummary=false; ULONG resumePoint = 0; @@ -1872,7 +1899,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(); @@ -1966,7 +2002,179 @@ int VompClientRRProc::processVDRShutdown() 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 diff --git a/vompclientrrproc.h b/vompclientrrproc.h index da6ef12..daa58cc 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,10 @@ class VompClientRRProc : public Thread int processDeleteTimer(); int processReScanRecording(); // FIXME obselete int processVDRShutdown(); + int processGetRecScraperEventType(); + int processGetScraperMovieInfo(); + int processGetScraperSeriesInfo(); + int processLoadTvMedia(); #endif int processLogin(); int processConfigSave(); @@ -101,7 +106,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; }; -- 2.39.2