2 Copyright 2019 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #ifndef VOMPSTANDALONE
24 #include <vdr/recording.h>
25 #include <vdr/channels.h>
26 #include <vdr/videodir.h>
27 #include <vdr/plugin.h>
28 #include <vdr/timers.h>
30 #include <vdr/remote.h>
31 #include "recplayer.h"
32 #include "mvpreceiver.h"
33 #include "services/scraper2vdr.h"
36 #include "vompclientrrproc.h"
37 #include "vompclient.h"
40 #include "mediaplayer.h"
41 #include "servermediafile.h"
43 #include "vdrcommand.h"
44 #include "picturereader.h"
48 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MIN = 0x00000302;
49 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MAX = 0x00000402;
51 // cc is release protocol version, increase with every release, that changes protocol
52 // dd is development protocol version, set to zero at every release,
53 // increase for every protocol change in git
54 // bb not equal zero should indicate a non loggytronic protocol
55 // aa is reserved for future use
56 // VOMP_PROTOCOL_VERSION_MIN is the protocol version minimal supported by the server
57 // VOMP_PROTOCOL_VERSION_MAX is the protocol version maximal supported by the server
58 // This allows to run older clients from a new server
59 // Increase the minimal protocol version everytime you break compatibility for a certain
63 /* Locking information from VDR:
65 + Instead of directly accessing the global variables Timers, Channels or Recordings,
66 they need to set up a cStateKey variable and call the proper getter function,
69 if (const cTimers *Timers = cTimers::GetTimersRead(StateKey)) {
75 if (cTimers *Timers = cTimers::GetTimersWrite(StateKey)) {
79 See timers.h, thread.h and tools.h for details on this new locking mechanism.
80 + There are convenience macros for easily accessing these lists without having
81 to explicitly set up a cStateKey and calling its Remove() function. These macros
82 have the form LOCK_*_READ/WRITE (with '*' being TIMERS, CHANNELS, SCHEDULES or
83 RECORDINGS). Simply put such a macro before the point where you need to access
84 the respective list, and there will be a pointer named Timers, Channels, Schedules
85 or Recordings, respectively, which is valid until the end of the current block.
86 + If a plugin needs to access several of the global lists in parallel, locking must
87 always be done in the sequence Timers, Channels, Recordings, Schedules. This is
88 necessary to make sure that different threads that need to lock several lists at
89 the same time don't end up in a deadlock.
93 // TODO: Use VDRs recording->ChangeName(option)) for move recording ?
95 ULONG VompClientRRProc::getProtocolVersionMin()
97 return VOMP_PROTOCOL_VERSION_MIN;
100 ULONG VompClientRRProc::getProtocolVersionMax()
102 return VOMP_PROTOCOL_VERSION_MAX;
105 VompClientRRProc::VompClientRRProc(VompClient& x)
108 log = Log::getInstance();
113 VompClientRRProc::~VompClientRRProc()
118 bool VompClientRRProc::init()
120 int a = threadStart();
125 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
129 Now we have a queue system is used,
130 since on rare occasion the client fire two request at once
131 e.g. heavily channel switching
132 then processing only a single request would cause a deadlock in the client
136 log->log("RRProc", Log::DEBUG, "recvReq");
138 req_queue.push(newRequest);
139 threadSignalNoLock();
140 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
146 void VompClientRRProc::threadMethod()
149 log->log("RRProc", Log::DEBUG, "threadMethod startup");
151 if (req_queue.size() != 0)
154 - log->log("RRProc", Log::ERR, "threadMethod err 1");
158 That was how the code used to be.
160 TODO: Work out why this happens.
163 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");
164 while (req_queue.size())
166 //log->log("RRProc", Log::DEBUG, "thread while");
167 req = req_queue.front();
170 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
172 if (!processPacket())
174 log->log("RRProc", Log::ERR, "processPacket exited with fail");
180 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");
186 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
187 threadWaitForSignal(); // unlocks, waits, relocks
188 if (req_queue.size() == 0)
190 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
195 // signalled with something in queue
197 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
199 while (req_queue.size())
201 //log->log("RRProc", Log::DEBUG, "thread while");
202 req = req_queue.front();
205 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
207 if (!processPacket())
209 log->log("RRProc", Log::ERR, "processPacket exited with fail");
216 // locked and run out of packets to process
220 bool VompClientRRProc::processPacket()
222 resp = new ResponsePacket();
223 if (!resp->init(req->requestID))
225 log->log("RRProc", Log::ERR, "response packet init fail");
228 if (req->data) free(req->data);
238 { // FIXME use defined names in vdrcommand.h for these cases
240 result = processLogin();
242 #ifndef VOMPSTANDALONE
244 result = processGetRecordingsList();
247 result = processDeleteRecording();
250 result = processDeleteRecResume();
253 result = processGetChannelsList();
256 result = processStartStreamingChannel();
259 result = processGetBlock();
262 result = processStopStreaming();
265 result = processStartStreamingRecording();
268 result = processGetChannelSchedule();
272 result = processConfigSave();
275 result = processConfigLoad();
277 #ifndef VOMPSTANDALONE
279 result = processReScanRecording(); // FIXME obselete
282 result = processGetTimers();
285 result = processSetTimer();
288 result = processPositionFromFrameNumber();
291 result = processFrameNumberFromPosition();
294 result = processMoveRecording();
297 result = processGetIFrame();
300 result = processGetRecInfo();
303 result = processGetRecInfo2();
306 result = processGetMarks();
309 result = processGetChannelPids();
312 result = processDeleteTimer();
315 result = processVDRShutdown();
317 case VDR_GETRECSCRAPEREVENTTYPE:
318 result = processGetRecScraperEventType();
320 case VDR_GETSCRAPERMOVIEINFO:
321 result = processGetScraperMovieInfo();
323 case VDR_GETSCRAPERSERIESINFO:
324 result = processGetScraperSeriesInfo();
326 case VDR_LOADTVMEDIA:
327 result = processLoadTvMedia();
329 case VDR_LOADTVMEDIARECTHUMB:
330 result = processLoadTvMediaRecThumb();
332 case VDR_GETEVENTSCRAPEREVENTTYPE:
333 result = processGetEventScraperEventType();
335 case VDR_LOADTVMEDIAEVENTTHUMB:
336 result = processLoadTvMediaEventThumb();
338 case VDR_LOADCHANNELLOGO:
339 result = processLoadChannelLogo();
342 case VDR_GETMEDIALIST:
343 result = processGetMediaList();
346 result = processOpenMedia();
348 case VDR_GETMEDIABLOCK:
349 result = processGetMediaBlock();
352 result = processGetLanguageList();
355 result = processGetLanguageContent();
357 case VDR_GETMEDIAINFO:
358 result = processGetMediaInfo();
360 case VDR_CLOSECHANNEL:
361 result = processCloseMediaChannel();
364 result = processSetCharset();
371 if (req->data) free(req->data);
375 if (result) return true;
380 int VompClientRRProc::processLogin()
382 if (req->dataLength != 6) return 0;
386 char configFileName[PATH_MAX];
387 snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", x.configDir, req->data[0], req->data[1], req->data[2], req->data[3], req->data[4], req->data[5]);
388 x.config.init(configFileName);
390 // Send the login reply
392 time_t timeNow = time(NULL);
393 struct tm* timeStruct = localtime(&timeNow);
394 int timeOffset = timeStruct->tm_gmtoff;
396 resp->addULONG(timeNow);
397 resp->addLONG(timeOffset);
398 resp->addULONG(VOMP_PROTOCOL_VERSION_MIN);
399 resp->addULONG(VOMP_PROTOCOL_VERSION_MAX);
401 // also send information about languages
402 resp->addULONG(I18nLanguages()->Size());
403 resp->addLONG(Setup.DisplaySubtitles);
404 for (int i=0;i < I18nLanguages()->Size(); i++) {
405 resp->addLONG(Setup.AudioLanguages[i]);
406 resp->addLONG(Setup.SubtitleLanguages[i]);
407 resp->addString(I18nLanguageCode(i));
411 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
412 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
415 x.netLog(); // safe to run here since the client won't start net logging for a while yet
420 int VompClientRRProc::processSetCharset()
422 int charset = ntohl(*(ULONG*)req->data);
423 if (charset>0 && charset<3)
425 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
426 x.setCharset(charset);
431 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
435 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
439 int VompClientRRProc::processConfigSave()
441 char* section = (char*)req->data;
445 for (UINT k = 0; k < req->dataLength; k++)
447 if (req->data[k] == '\0')
451 key = (char*)&req->data[k+1];
455 value = (char*)&req->data[k+1];
461 // if the last string (value) doesnt have null terminator, give up
462 if (req->data[req->dataLength - 1] != '\0') return 0;
464 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
465 if (x.config.setValueString(section, key, value))
475 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
480 int VompClientRRProc::processConfigLoad()
482 char* section = (char*)req->data;
485 for (UINT k = 0; k < req->dataLength; k++)
487 if (req->data[k] == '\0')
489 key = (char*)&req->data[k+1];
494 char* value = x.config.getValueString(section, key);
498 resp->addString(value);//client coding, do not touch
499 log->log("RRProc", Log::DEBUG, "Written config load packet");
505 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
509 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
515 //helper for sending from a serialize buffer
516 //insert the used len into the first 4 Bytes of the buffer
517 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
518 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
520 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
524 * media List Request:
525 * Media List response:
528 #define MLISTBUF 500000
529 int VompClientRRProc::processGetMediaList()
531 SerializeBuffer buffer(req->data,req->dataLength);
532 MediaURI uri(0,NULL,NULL);
533 VDR_GetMediaListRequest request(&uri);
534 if (request.deserialize(&buffer) != 0) {
535 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
538 const char *dirname=uri.getName();
539 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
542 if (dirname == NULL) {
543 ml=x.media->getRootList();
545 ml=x.media->getMediaList(&uri);
548 log->log("Client", Log::ERR, "getMediaList returned NULL");
551 SerializeBuffer rbuf(MLISTBUF,false,true);
552 ULONG flags=0; //TODO: real error handling by setting flags
553 VDR_GetMediaListResponse response(&flags,ml);
554 if (response.serialize(&rbuf) != 0) {
555 log->log("Client", Log::ERR, "getMediaList returned NULL");
559 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
564 log->log("Client", Log::DEBUG, "Written Media list");
569 * openMedia response:
571 int VompClientRRProc::processOpenMedia()
573 SerializeBuffer buffer(req->data,req->dataLength);
574 MediaURI uri(0,NULL,NULL);
578 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
579 if (request.deserialize(&buffer) != 0) {
580 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
583 const char *name=uri.getName();
584 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
586 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
591 log->log("Client", Log::ERR, "openMediaRequest unable to open");
593 VDR_OpenMediumResponse response(&flags,&size);
594 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
595 if (response.serialize(&rbuf) != 0) {
596 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
599 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
606 * packet - no serialized response!
608 int VompClientRRProc::processGetMediaBlock()
610 SerializeBuffer buffer(req->data,req->dataLength);
614 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
615 if (request.deserialize(&buffer) != 0) {
616 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
619 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
621 UCHAR sendBuffer[amount ];
622 ULONG amountReceived = 0;
623 UCHAR *rbuf=sendBuffer;
624 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
625 if (!amountReceived || rt != 0)
627 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
631 if (rbuf != sendBuffer) {
632 //the provider did not use the optimized handling with using my buffer
633 resp->copyin(rbuf,amountReceived);
636 // the provider did not allocate a new buffer
637 resp->copyin(sendBuffer,amountReceived);
641 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
642 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
649 int VompClientRRProc::processGetMediaInfo()
651 SerializeBuffer buffer(req->data,req->dataLength);
653 VDR_GetMediaInfoRequest request(&channel);
654 if (request.deserialize(&buffer) != 0) {
655 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
658 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
661 int rt=x.media->getMediaInfo(channel,&mi);
664 log->log("Client", Log::ERR, "getMediaInfo unable to get");
666 VDR_GetMediaInfoResponse response(&flags,&mi);
667 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
668 if (response.serialize(&rbuf) != 0) {
669 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
681 int VompClientRRProc::processCloseMediaChannel()
683 SerializeBuffer buffer(req->data,req->dataLength);
685 VDR_CloseMediaChannelRequest request(&channel);
686 if (request.deserialize(&buffer) != 0) {
687 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
691 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
692 int rt=x.media->closeMediaChannel(channel);
695 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
697 VDR_CloseMediaChannelResponse response(&flags);
698 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
699 if (response.serialize(&rbuf) != 0) {
700 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
709 int VompClientRRProc::processGetLanguageList()
711 x.i18n.findLanguages();
712 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
714 I18n::lang_code_list::const_iterator iter;
715 for (iter = languages.begin(); iter != languages.end(); ++iter)
717 resp->addString(iter->first.c_str()); // Source code is acsii
718 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
721 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
725 int VompClientRRProc::processGetLanguageContent()
727 if (req->dataLength <= 0) return 0;
728 std::string code, result;
729 code.assign((char*)req->data, req->dataLength - 1);
730 x.i18n.findLanguages();
731 I18n::trans_table texts = x.i18n.getLanguageContent(code);
732 I18n::trans_table::const_iterator iter;
733 for (iter = texts.begin(); iter != texts.end(); ++iter)
735 resp->addString(iter->first.c_str());// source code is acsii since it is english
736 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
739 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
743 #ifndef VOMPSTANDALONE
745 int VompClientRRProc::processGetRecordingsList()
748 #if APIVERSNUM > 20101
749 int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB);
751 int Percent = VideoDiskSpace(&FreeMB);
753 int Total = (FreeMB / (100 - Percent)) * 100;
755 resp->addULONG(Total);
756 resp->addULONG(FreeMB);
757 resp->addULONG(Percent);
759 #if VDRVERSNUM >= 20301
760 LOCK_RECORDINGS_READ;
761 const cRecordings* tRecordings = Recordings;
763 cThreadLock RecordingsLock(&Recordings);
764 const cRecordings* tRecordings = &Recordings;
767 for (const cRecording *recording = tRecordings->First(); recording; recording = tRecordings->Next(recording))
769 #if VDRVERSNUM < 10721
770 resp->addULONG(recording->start);
772 resp->addULONG(recording->Start());
774 resp->addUCHAR(recording->IsNew() ? 1 : 0);
775 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
776 resp->addString(recording->FileName());//file name are not visible by user do not touch
780 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
782 log->log("RRProc", Log::DEBUG, "Written recordings list");
787 int VompClientRRProc::processDeleteRecording()
789 // data is a pointer to the fileName string
791 #if VDRVERSNUM >= 20301
792 LOCK_RECORDINGS_WRITE;
793 cRecordings* tRecordings = Recordings;
795 cThreadLock RecordingsLock(&Recordings);
796 cRecordings* tRecordings = &Recordings;
799 cRecording* recording = tRecordings->GetByName((char*)req->data);
801 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
805 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
807 // TODO: Switch to using: cRecording::IsInUse(void) const
808 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
811 if (recording->Delete())
813 #if VDRVERSNUM >= 20301
814 tRecordings->DelByName(recording->FileName());
815 tRecordings->SetModified();
816 #elif VDRVERSNUM > 10300
817 // Copy svdrp's way of doing this, see if it works
818 ::Recordings.DelByName(recording->FileName());
838 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
843 int VompClientRRProc::processDeleteRecResume()
845 // data is a pointer to the fileName string
847 #if VDRVERSNUM < 20301
848 resp->addULONG(5); // Not supported
850 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
855 const cRecordings* Recordings = cRecordings::GetRecordingsRead(StateKey);
856 const cRecording* recording = Recordings->GetByName((char*)req->data);
860 log->log("RRProc", Log::DEBUG, "deleting recording resume : %s", recording->Name());
862 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
865 if (ResumeFile.Read() >= 0)
868 resp->addULONG(1); // success
872 resp->addULONG(2); // failed, no resume point saved
878 resp->addULONG(4); // failed to find recording
882 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
887 int VompClientRRProc::processMoveRecording()
889 log->log("RRProc", Log::DEBUG, "Process move recording");
890 char* fileName = (char*)req->data;
891 char* newPath = NULL;
893 for (UINT k = 0; k < req->dataLength; k++)
895 if (req->data[k] == '\0')
897 newPath = (char*)&req->data[k+1];
901 if (!newPath) return 0;
904 #if VDRVERSNUM >= 20301
905 LOCK_RECORDINGS_WRITE;
906 cRecordings* tRecordings = Recordings;
908 cThreadLock RecordingsLock(&Recordings);
909 cRecordings* tRecordings = &Recordings;
912 cRecording* recording = tRecordings->GetByName((char*)fileName);
914 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
918 // TODO: Switch to using: int cRecording::IsInUse(void) const
919 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
922 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
923 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
924 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
926 const char* t = recording->FileName();
928 char* dateDirName = NULL; int k;
929 char* titleDirName = NULL; int j;
931 // Find the datedirname
932 for(k = strlen(t) - 1; k >= 0; k--)
936 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
937 dateDirName = new char[strlen(&t[k+1]) + 1];
938 strcpy(dateDirName, &t[k+1]);
943 // Find the titledirname
945 for(j = k-1; j >= 0; j--)
949 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
950 titleDirName = new char[(k - j - 1) + 1];
951 memcpy(titleDirName, &t[j+1], k - j - 1);
952 titleDirName[k - j - 1] = '\0';
957 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
958 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
959 #if APIVERSNUM > 20101
960 log->log("RRProc", Log::DEBUG, "viddir: %s", cVideoDirectory::Name());
962 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
965 char* newPathConv = (char*)malloc(strlen(newPath)+1);
966 strcpy(newPathConv, newPath);
967 newPathConv = ExchangeChars(newPathConv, true);
968 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
970 #if APIVERSNUM > 20101
971 char* newContainer = new char[strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1];
972 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1);
973 sprintf(newContainer, "%s%s%s", cVideoDirectory::Name(), newPathConv, titleDirName);
975 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
976 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
977 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
981 log->log("RRProc", Log::DEBUG, "%s", newContainer);
984 int statret = stat(newContainer, &dstat);
985 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
987 log->log("RRProc", Log::DEBUG, "new dir does not exist");
988 int mkdirret = mkdir(newContainer, 0755);
991 delete[] dateDirName;
992 delete[] titleDirName;
993 delete[] newContainer;
997 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1001 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
1003 delete[] dateDirName;
1004 delete[] titleDirName;
1005 delete[] newContainer;
1009 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1014 // Ok, the directory container has been made, or it pre-existed.
1016 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
1017 sprintf(newDir, "%s/%s", newContainer, dateDirName);
1019 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
1020 int renameret = rename(t, newDir);
1023 // Success. Test for remove old dir containter
1024 char* oldTitleDir = new char[k+1];
1025 memcpy(oldTitleDir, t, k);
1026 oldTitleDir[k] = '\0';
1027 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
1028 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
1029 delete[] oldTitleDir;
1031 #if VDRVERSNUM >= 20301
1032 tRecordings->SetModified();
1033 #elif VDRVERSNUM > 10311
1034 ::Recordings.Update();
1036 // Success. Send a different packet from just a ulong
1037 resp->addULONG(1); // success
1038 resp->addString(newDir); //system depent do not convert
1046 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1048 delete[] dateDirName;
1049 delete[] titleDirName;
1050 delete[] newContainer;
1057 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1064 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1070 int VompClientRRProc::processGetChannelsList()
1074 char* chanConfig = x.config.getValueString("General", "Channels");
1076 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
1078 #if VDRVERSNUM >= 20301
1080 const cChannels* tChannels = Channels;
1082 const cChannels* tChannels = &Channels;
1085 for (const cChannel *channel = tChannels->First(); channel; channel = tChannels->Next(channel))
1087 #if VDRVERSNUM < 10300
1088 if (!channel->GroupSep() && (!channel->Ca() || allChans))
1090 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
1093 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
1095 if (channel->Vpid()) type = 1;
1096 #if VDRVERSNUM < 10300
1099 else if (channel->Apid(0)) type = 2;
1103 resp->addULONG(channel->Number());
1104 resp->addULONG(type);
1105 resp->addString(x.charconvsys->Convert(channel->Name()));
1106 #if VDRVERSNUM < 10703
1109 resp->addULONG(channel->Vtype());
1115 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1117 log->log("RRProc", Log::DEBUG, "Written channels list");
1122 int VompClientRRProc::processGetChannelPids()
1124 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1126 #if VDRVERSNUM >= 20301
1128 const cChannels* tChannels = Channels;
1130 cChannels* tChannels = &Channels;
1133 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1138 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1147 #if VDRVERSNUM < 10300
1149 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
1150 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
1152 if (channel->Apid2())
1154 else if (channel->Apid1())
1161 for (const int *Apid = channel->Apids(); *Apid; Apid++)
1165 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
1169 for (const int *Spid = channel->Spids(); *Spid; Spid++)
1176 // Format of response
1195 resp->addULONG(channel->Vpid());
1196 #if VDRVERSNUM < 10703
1199 resp->addULONG(channel->Vtype());
1201 resp->addULONG(numApids);
1203 #if VDRVERSNUM < 10300
1206 resp->addULONG(channel->Apid1());
1207 resp->addString("");
1211 resp->addULONG(channel->Apid2());
1212 resp->addString("");
1217 for (ULONG i = 0; i < numApids; i++)
1219 resp->addULONG(channel->Apid(i));
1220 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1222 resp->addULONG(numDpids);
1223 for (ULONG i = 0; i < numDpids; i++)
1225 resp->addULONG(channel->Dpid(i));
1226 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1228 resp->addULONG(numSpids);
1229 for (ULONG i = 0; i < numSpids; i++)
1231 resp->addULONG(channel->Spid(i));
1232 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1235 resp->addULONG(channel->Tpid());
1236 // Format of extended response, for compatibility with older client at the end
1248 #if VDRVERSNUM < 10300
1258 for (ULONG i = 0; i < numApids; i++)
1260 #if VDRVERSNUM < 10715
1263 resp->addULONG(channel->Atype(i));
1266 for (ULONG i = 0; i < numDpids; i++)
1268 #if VDRVERSNUM < 10715
1269 resp->addULONG(0x6A /*AC3*/);
1271 resp->addULONG(channel->Dtype(i));
1274 for (ULONG i = 0; i < numSpids; i++)
1276 #if VDRVERSNUM < 10715
1281 resp->addULONG(channel->SubtitlingType(i));
1282 resp->addULONG(channel->CompositionPageId(i));
1283 resp->addULONG(channel->AncillaryPageId(i));
1290 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1292 log->log("RRProc", Log::DEBUG, "Written channels pids");
1297 int VompClientRRProc::processStartStreamingChannel()
1301 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1305 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1306 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1308 #if VDRVERSNUM >= 20301
1310 const cChannels* tChannels = Channels;
1312 cChannels* tChannels = &Channels;
1315 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1320 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1324 // get the priority we should use
1326 int priority = x.config.getValueLong("General", "Live priority", &fail);
1329 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1333 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1337 // a bit of sanity..
1338 #if VDRVERSNUM < 10725
1339 if (priority < 0) priority = 0;
1341 if (priority < -99) priority = -99;
1343 if (priority > 99) priority = 99;
1345 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1346 x.lp = MVPReceiver::create(channel, priority);
1352 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1356 if (!x.lp->init(&x.tcp, req->requestID))
1362 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1368 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1372 int VompClientRRProc::processStopStreaming()
1374 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1377 x.lp->detachMVPReceiver();
1381 else if (x.recplayer)
1383 x.writeResumeData();
1391 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1395 int VompClientRRProc::processGetBlock()
1399 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1405 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1409 UCHAR* data = req->data;
1411 ULLONG position = x.ntohll(*(ULLONG*)data);
1412 data += sizeof(ULLONG);
1413 ULONG amount = ntohl(*(ULONG*)data);
1415 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1417 UCHAR sendBuffer[amount];
1418 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1420 if (!amountReceived)
1423 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1427 resp->copyin(sendBuffer, amountReceived);
1428 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1432 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1433 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1437 int VompClientRRProc::processStartStreamingRecording()
1439 // data is a pointer to the fileName string
1441 #if VDRVERSNUM >= 20301
1442 LOCK_RECORDINGS_READ;
1443 const cRecordings* tRecordings = Recordings;
1445 cThreadLock RecordingsLock(&Recordings);
1446 cRecordings* tRecordings = &Recordings;
1449 const cRecording* recording = tRecordings->GetByName((char*)req->data);
1451 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1455 x.recplayer = new RecPlayer(recording);
1457 resp->addULLONG(x.recplayer->getLengthBytes());
1458 resp->addULONG(x.recplayer->getLengthFrames());
1460 #if VDRVERSNUM < 10703
1461 resp->addUCHAR(true);//added for TS
1463 resp->addUCHAR(recording->IsPesRecording());//added for TS
1467 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1469 log->log("RRProc", Log::DEBUG, "written totalLength");
1473 // No such recording exists
1476 resp->addUCHAR(false);
1478 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1479 log->log("RRProc", Log::DEBUG, "start streaming recording failed");
1485 int VompClientRRProc::processPositionFromFrameNumber()
1489 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1493 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1497 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1500 resp->addULLONG(retval);
1502 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1504 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1508 int VompClientRRProc::processFrameNumberFromPosition()
1512 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1516 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1520 retval = x.recplayer->frameNumberFromPosition(position);
1523 resp->addULONG(retval);
1525 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1527 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1531 int VompClientRRProc::processGetIFrame()
1533 bool success = false;
1535 ULONG* data = (ULONG*)req->data;
1537 ULONG frameNumber = ntohl(*data);
1539 ULONG direction = ntohl(*data);
1541 ULLONG rfilePosition = 0;
1542 ULONG rframeNumber = 0;
1543 ULONG rframeLength = 0;
1547 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1551 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1554 // returns file position, frame number, length
1558 resp->addULLONG(rfilePosition);
1559 resp->addULONG(rframeNumber);
1560 resp->addULONG(rframeLength);
1568 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1570 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1574 int VompClientRRProc::processGetChannelSchedule()
1576 ULONG* data = (ULONG*)req->data;
1578 ULONG channelNumber = ntohl(*data);
1580 ULONG startTime = ntohl(*data);
1582 ULONG duration = ntohl(*data);
1584 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1586 #if VDRVERSNUM >= 20301
1588 const cChannels* tChannels = Channels;
1590 cChannels* tChannels = &Channels;
1593 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1598 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1600 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1604 log->log("RRProc", Log::DEBUG, "Got channel");
1606 #if VDRVERSNUM < 10300
1607 cMutexLock MutexLock;
1608 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
1609 #elif VDRVERSNUM < 20301
1610 cSchedulesLock MutexLock;
1611 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
1613 LOCK_SCHEDULES_READ;
1614 const cSchedules *tSchedules = Schedules;
1621 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1623 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1627 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1629 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
1634 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1636 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1640 log->log("RRProc", Log::DEBUG, "Got schedule object");
1642 const char* empty = "";
1643 bool atLeastOneEvent = false;
1646 ULONG thisEventTime;
1647 ULONG thisEventDuration;
1648 const char* thisEventTitle;
1649 const char* thisEventSubTitle;
1650 const char* thisEventDescription;
1652 #if VDRVERSNUM < 10300
1654 const cEventInfo *event;
1655 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1657 event = Schedule->GetEventNumber(eventNumber);
1659 thisEventID = event->GetEventID();
1660 thisEventTime = event->GetTime();
1661 thisEventDuration = event->GetDuration();
1662 thisEventTitle = event->GetTitle();
1663 thisEventSubTitle = event->GetSubtitle();
1664 thisEventDescription = event->GetExtendedDescription();
1668 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1670 thisEventID = event->EventID();
1671 thisEventTime = event->StartTime();
1672 thisEventDuration = event->Duration();
1673 thisEventTitle = event->Title();
1674 thisEventSubTitle = NULL;
1675 thisEventDescription = event->Description();
1679 //in the past filter
1680 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1683 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1686 if (thisEventTime >= (startTime + duration)) continue;
1688 if (!thisEventTitle) thisEventTitle = empty;
1689 if (!thisEventSubTitle) thisEventSubTitle = empty;
1690 if (!thisEventDescription) thisEventDescription = empty;
1692 resp->addULONG(thisEventID);
1693 resp->addULONG(thisEventTime);
1694 resp->addULONG(thisEventDuration);
1696 resp->addString(x.charconvsys->Convert(thisEventTitle));
1697 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1698 resp->addString(x.charconvsys->Convert(thisEventDescription));
1700 atLeastOneEvent = true;
1703 log->log("RRProc", Log::DEBUG, "Got all event data");
1705 if (!atLeastOneEvent)
1708 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1712 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1714 log->log("RRProc", Log::DEBUG, "written schedules packet");
1719 int VompClientRRProc::processGetTimers()
1721 #if VDRVERSNUM >= 20301
1723 const cTimers* tTimers = Timers;
1725 const cTimers* tTimers = &Timers;
1728 const cTimer *timer;
1729 int numTimers = tTimers->Count();
1731 resp->addULONG(numTimers);
1733 for (int i = 0; i < numTimers; i++)
1735 timer = tTimers->Get(i);
1737 #if VDRVERSNUM < 10300
1738 resp->addULONG(timer->Active());
1740 resp->addULONG(timer->HasFlags(tfActive));
1742 resp->addULONG(timer->Recording());
1743 resp->addULONG(timer->Pending());
1744 resp->addULONG(timer->Priority());
1745 resp->addULONG(timer->Lifetime());
1746 resp->addULONG(timer->Channel()->Number());
1747 resp->addULONG(timer->StartTime());
1748 resp->addULONG(timer->StopTime());
1749 resp->addULONG(timer->Day());
1750 resp->addULONG(timer->WeekDays());
1751 resp->addString(timer->File()); //Filename is system specific and not visible by user
1755 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1757 log->log("RRProc", Log::DEBUG, "Written timers list");
1762 int VompClientRRProc::processSetTimer()
1764 char* timerString = new char[strlen((char*)req->data) + 1];
1765 strcpy(timerString, (char*)req->data);
1767 #if VDRVERSNUM < 10300
1769 // If this is VDR 1.2 the date part of the timer string must be reduced
1770 // to just DD rather than YYYY-MM-DD
1772 int s = 0; // source
1773 int d = 0; // destination
1775 while(c != 2) // copy up to date section, including the second ':'
1777 timerString[d] = req->data[s];
1778 if (req->data[s] == ':') c++;
1782 // now it has copied up to the date section
1784 while(c != 2) // waste YYYY-MM-
1786 if (req->data[s] == '-') c++;
1789 // now source is at the DD
1790 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1791 d += req->dataLength - s;
1792 timerString[d] = '\0';
1794 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1797 log->log("RRProc", Log::DEBUG, "%s", timerString);
1799 cTimer *timer = new cTimer;
1800 if (!timer->Parse((char*)timerString))
1804 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1809 #if VDRVERSNUM >= 20301
1811 cTimers* tTimers = Timers;
1813 cTimers* tTimers = &Timers;
1816 cTimer *t = tTimers->GetTimer(timer);
1821 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1826 timer->ClrFlags(tfRecording);
1827 tTimers->Add(timer);
1828 #if VDRVERSNUM < 10300
1830 #elif VDRVERSNUM < 20301
1831 tTimers->SetModified();
1836 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1840 int VompClientRRProc::processDeleteTimer()
1842 log->log("RRProc", Log::DEBUG, "Delete timer called");
1847 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1848 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1849 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1850 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1851 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1853 #if VDRVERSNUM >= 20301
1855 cTimers* tTimers = Timers;
1857 cTimers* tTimers = &Timers;
1861 for (ti = tTimers->First(); ti; ti = tTimers->Next(ti))
1863 if ( (ti->Channel()->Number() == delChannel)
1864 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1865 && (ti->StartTime() == delStart)
1866 && (ti->StopTime() == delStop) )
1874 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1878 #if VDRVERSNUM < 20301
1879 // I suppose with the new locking this just can't happen
1880 if (tTimers->BeingEdited())
1882 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1885 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1890 if (ti->Recording())
1892 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1895 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1900 tTimers->SetModified();
1904 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1908 int VompClientRRProc::processGetRecInfo()
1910 // data is a pointer to the fileName string
1911 #if VDRVERSNUM >= 20301
1912 LOCK_RECORDINGS_READ;
1913 const cRecordings* tRecordings = Recordings;
1915 cThreadLock RecordingsLock(&Recordings);
1916 cRecordings* tRecordings = &Recordings;
1919 const cRecording *recording = tRecordings->GetByName((char*)req->data);
1921 time_t timerStart = 0;
1922 time_t timerStop = 0;
1923 char* summary = NULL;
1924 char* shorttext = NULL;
1925 char* description = NULL;
1927 bool newsummary=false;
1928 ULONG resumePoint = 0;
1932 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1935 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1940 4 bytes: start time for timer
1941 4 bytes: end time for timer
1942 4 bytes: resume point
1944 4 bytes: num components
1951 8 bytes: frames per second
1955 // Get current timer
1957 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1960 timerStart = rc->Timer()->StartTime();
1961 timerStop = rc->Timer()->StopTime();
1962 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1965 resp->addULONG(timerStart);
1966 resp->addULONG(timerStop);
1970 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1973 resumePoint = strtoul(value, NULL, 10);
1977 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1980 ResumeId = atoi(ResumeIdC);
1984 ResumeId = 0; //default if not defined in vomp-MAC.conf
1986 while (ResumeIDLock)
1987 cCondWait::SleepMs(100);
1988 ResumeIDLock = true;
1989 int OldSetupResumeID = Setup.ResumeID;
1990 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1991 #if VDRVERSNUM < 10703
1992 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1994 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1996 Setup.ResumeID = OldSetupResumeID; //and restore it back
1997 ResumeIDLock = false;
1999 int resume = ResumeFile.Read();
2000 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2002 resumePoint = ResumeFile.Read();
2004 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2006 resp->addULONG(resumePoint);
2010 #if VDRVERSNUM < 10300
2011 summary = (char*)recording->Summary();
2013 const cRecordingInfo *Info = recording->Info();
2014 shorttext = (char*)Info->ShortText();
2015 description = (char*) (char*)Info->Description();
2016 if (isempty(shorttext)) summary=description;
2017 else if (isempty(description)) summary=shorttext;
2019 int length=strlen(description)+strlen(shorttext)+4;
2020 summary=new char[length];
2021 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2025 if (isempty(summary)) summary = (char*)Info->Description();
2027 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2030 resp->addString(x.charconvsys->Convert(summary));
2031 if (newsummary) delete [] summary;
2035 resp->addString("");
2040 #if VDRVERSNUM < 10300
2042 // Send 0 for numchannels - this signals the client this info is not available
2046 const cComponents* components = Info->Components();
2048 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2056 resp->addULONG(components->NumComponents());
2058 tComponent* component;
2059 for (int i = 0; i < components->NumComponents(); i++)
2061 component = components->Component(i);
2063 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2065 resp->addUCHAR(component->stream);
2066 resp->addUCHAR(component->type);
2068 if (component->language)
2070 resp->addString(x.charconvsys->Convert(component->language));
2074 resp->addString("");
2076 if (component->description)
2078 resp->addString(x.charconvsys->Convert(component->description));
2082 resp->addString("");
2088 double framespersec;
2089 #if VDRVERSNUM < 10703
2090 framespersec = FRAMESPERSEC;
2092 framespersec = Info->FramesPerSecond();
2094 resp->adddouble(framespersec);
2095 title = (char*)Info->Title();
2098 resp->addString(x.charconvsys->Convert(title));
2102 resp->addString(x.charconvsys->Convert(recording->Name()));
2108 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2110 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2115 // TODO Remove getrecinfo(1) for version 0.6.0 and rename getrecinfo2 to getrecinfo
2117 int VompClientRRProc::processGetRecInfo2()
2119 // data is a pointer to the fileName string
2120 #if VDRVERSNUM >= 20301
2121 LOCK_RECORDINGS_READ;
2122 const cRecordings* tRecordings = Recordings;
2124 cThreadLock RecordingsLock(&Recordings);
2125 cRecordings* tRecordings = &Recordings;
2128 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2130 time_t timerStart = 0;
2131 time_t timerStop = 0;
2132 char* summary = NULL;
2133 char* shorttext = NULL;
2134 char* description = NULL;
2136 bool newsummary=false;
2137 ULONG resumePoint = 0;
2141 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
2144 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2149 4 bytes: start time for timer
2150 4 bytes: end time for timer
2151 4 bytes: resume point
2153 4 bytes: num components
2160 8 bytes: frames per second
2162 // new stuff starts here
2163 string: channel name
2170 // Get current timer
2172 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
2175 timerStart = rc->Timer()->StartTime();
2176 timerStop = rc->Timer()->StopTime();
2177 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
2180 resp->addULONG(timerStart);
2181 resp->addULONG(timerStop);
2185 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
2188 resumePoint = strtoul(value, NULL, 10);
2192 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
2195 ResumeId = atoi(ResumeIdC);
2199 ResumeId = 0; //default if not defined in vomp-MAC.conf
2201 while (ResumeIDLock)
2202 cCondWait::SleepMs(100);
2203 ResumeIDLock = true;
2204 int OldSetupResumeID = Setup.ResumeID;
2205 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
2206 #if VDRVERSNUM < 10703
2207 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
2209 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
2211 Setup.ResumeID = OldSetupResumeID; //and restore it back
2212 ResumeIDLock = false;
2214 int resume = ResumeFile.Read();
2215 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2217 resumePoint = ResumeFile.Read();
2219 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2221 resp->addULONG(resumePoint);
2225 #if VDRVERSNUM < 10300
2226 summary = (char*)recording->Summary();
2228 const cRecordingInfo *Info = recording->Info();
2229 shorttext = (char*)Info->ShortText();
2230 description = (char*) (char*)Info->Description();
2231 if (isempty(shorttext)) summary=description;
2232 else if (isempty(description)) summary=shorttext;
2234 int length=strlen(description)+strlen(shorttext)+4;
2235 summary=new char[length];
2236 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2240 if (isempty(summary)) summary = (char*)Info->Description();
2242 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2245 resp->addString(x.charconvsys->Convert(summary));
2246 if (newsummary) delete [] summary;
2250 resp->addString("");
2255 #if VDRVERSNUM < 10300
2257 // Send 0 for numchannels - this signals the client this info is not available
2261 const cComponents* components = Info->Components();
2263 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2271 resp->addULONG(components->NumComponents());
2273 tComponent* component;
2274 for (int i = 0; i < components->NumComponents(); i++)
2276 component = components->Component(i);
2278 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2280 resp->addUCHAR(component->stream);
2281 resp->addUCHAR(component->type);
2283 if (component->language)
2285 resp->addString(x.charconvsys->Convert(component->language));
2289 resp->addString("");
2291 if (component->description)
2293 resp->addString(x.charconvsys->Convert(component->description));
2297 resp->addString("");
2303 double framespersec;
2304 #if VDRVERSNUM < 10703
2305 framespersec = FRAMESPERSEC;
2307 framespersec = Info->FramesPerSecond();
2309 resp->adddouble(framespersec);
2310 title = (char*)Info->Title();
2313 resp->addString(x.charconvsys->Convert(title));
2317 resp->addString(x.charconvsys->Convert(recording->Name()));
2321 if (Info->ChannelName())
2323 resp->addString(x.charconvsys->Convert(Info->ChannelName()));
2327 resp->addString("");
2330 resp->addULONG(recording->LengthInSeconds());
2331 resp->addULONG(recording->FileSizeMB());
2332 resp->addULONG(recording->Priority());
2333 resp->addULONG(recording->Lifetime());
2338 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2340 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2347 int VompClientRRProc::processReScanRecording()
2351 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
2355 x.recplayer->scan();
2357 resp->addULLONG(x.recplayer->getLengthBytes());
2358 resp->addULONG(x.recplayer->getLengthFrames());
2360 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2361 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
2365 // FIXME without client calling rescan, getblock wont work even tho more data is avail
2367 int VompClientRRProc::processGetMarks()
2369 // data is a pointer to the fileName string
2370 #if VDRVERSNUM >= 20301
2371 LOCK_RECORDINGS_READ;
2372 const cRecordings* tRecordings = Recordings;
2374 cThreadLock RecordingsLock(&Recordings);
2375 cRecordings* tRecordings = &Recordings;
2378 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2379 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
2384 #if VDRVERSNUM < 10703
2385 Marks.Load(recording->FileName());
2387 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
2391 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
2393 #if VDRVERSNUM < 10721
2394 ULLONG mposition = m->position;
2396 ULLONG mposition = m->Position();
2398 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
2400 resp->addULONG(mposition);
2405 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
2411 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2413 log->log("RRProc", Log::DEBUG, "Written Marks list");
2418 int VompClientRRProc::processVDRShutdown()
2420 log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
2421 VompClient::decClients(); // Temporarily make this client disappear
2422 cRemote::Put(kPower);
2423 VompClient::incClients();
2425 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2429 int VompClientRRProc::processGetRecScraperEventType()
2431 #if VDRVERSNUM >= 20301
2432 LOCK_RECORDINGS_READ;
2433 const cRecordings* tRecordings = Recordings;
2435 cThreadLock RecordingsLock(&Recordings);
2436 cRecordings* tRecordings = &Recordings;
2439 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2440 ScraperGetEventType call;
2443 if (recording && x.scrapQuery())
2445 call.recording = recording;
2446 x.scraper->Service("GetEventType", &call);
2448 resp->addUCHAR(call.type);
2449 if (call.type == tMovie)
2451 resp->addLONG(call.movieId);
2453 else if (call.type == tSeries)
2455 resp->addLONG(call.seriesId);
2456 resp->addLONG(call.episodeId);
2459 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2464 int VompClientRRProc::processGetEventScraperEventType()
2466 ScraperGetEventType call;
2468 ULONG channelid = ntohl(*(ULONG*)req->data);
2469 ULONG eventid = ntohl(*(ULONG*)(req->data+4));
2470 const cEvent *event = NULL;
2472 #if VDRVERSNUM >= 20301
2474 const cChannels* tChannels = Channels;
2476 cChannels* tChannels = &Channels;
2479 const cChannel* channel = tChannels->GetByNumber(channelid);
2481 #if VDRVERSNUM < 10300
2482 cMutexLock MutexLock;
2483 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
2484 #elif VDRVERSNUM < 20301
2485 cSchedulesLock MutexLock;
2486 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
2488 LOCK_SCHEDULES_READ;
2489 const cSchedules *tSchedules = Schedules;
2492 if (tSchedules && channel)
2494 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
2496 event = Schedule->GetEvent(eventid);
2500 if (event && x.scrapQuery())
2503 x.scraper->Service("GetEventType",&call);
2505 resp->addUCHAR(call.type);
2506 if (call.type == tMovie)
2508 resp->addLONG(call.movieId);
2509 } else if (call.type == tSeries){
2510 resp->addLONG(call.seriesId);
2511 resp->addLONG(call.episodeId);
2513 if (x.pict->epgImageExists(eventid)) {
2520 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2525 #define ADDSTRING_TO_PAKET(y) if ((y)!=0) resp->addString(x.charconvutf8->Convert(y)); else resp->addString("");
2527 int VompClientRRProc::processGetScraperMovieInfo()
2531 movie.movieId = ntohl(*(ULONG*)req->data);
2532 if (!x.scrapQuery()) {
2533 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2534 return 0; //stupid, I have no scraper why are you still asking
2536 x.scraper->Service("GetMovie",&movie);
2539 ADDSTRING_TO_PAKET(movie.title.c_str());
2540 ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
2541 ADDSTRING_TO_PAKET(movie.tagline.c_str());
2542 ADDSTRING_TO_PAKET(movie.overview.c_str());
2543 resp->addUCHAR(movie.adult);
2544 ADDSTRING_TO_PAKET(movie.collectionName.c_str());
2546 resp->addLONG(movie.budget);
2547 resp->addLONG(movie.revenue);
2548 ADDSTRING_TO_PAKET(movie.genres.c_str());
2549 ADDSTRING_TO_PAKET(movie.homepage.c_str());
2550 ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
2551 resp->addLONG(movie.runtime);
2552 resp->adddouble(movie.popularity);
2553 resp->adddouble(movie.voteAverage);
2554 resp->addULONG(movie.poster.width);
2555 resp->addULONG(movie.poster.height);
2556 resp->addULONG(movie.fanart.width);
2557 resp->addULONG(movie.fanart.height);
2558 resp->addULONG(movie.collectionPoster.width);
2559 resp->addULONG(movie.collectionPoster.height);
2560 resp->addULONG(movie.collectionFanart.width);
2561 resp->addULONG(movie.collectionFanart.height);
2562 resp->addULONG(movie.actors.size());
2563 for (ULONG acty=0; acty < movie.actors.size(); acty++) {
2564 ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
2565 ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
2566 resp->addULONG(movie.actors[acty].actorThumb.width);
2567 resp->addULONG(movie.actors[acty].actorThumb.height);
2571 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2578 int VompClientRRProc::processGetScraperSeriesInfo()
2581 series.seriesId = ntohl(*(ULONG*)req->data);
2582 series.episodeId = ntohl(*(ULONG*)(req->data+4));
2583 if (!x.scrapQuery()) {
2584 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2585 return 0; //stupid, I have no scraper why are you still asking
2587 x.scraper->Service("GetSeries",&series);
2589 ADDSTRING_TO_PAKET(series.name.c_str());
2590 ADDSTRING_TO_PAKET(series.overview.c_str());
2591 ADDSTRING_TO_PAKET(series.firstAired.c_str());
2592 ADDSTRING_TO_PAKET(series.network.c_str());
2593 ADDSTRING_TO_PAKET(series.genre.c_str());
2594 resp->adddouble(series.rating);
2595 ADDSTRING_TO_PAKET(series.status.c_str());
2597 resp->addLONG(series.episode.number);
2598 resp->addLONG(series.episode.season);
2599 ADDSTRING_TO_PAKET(series.episode.name.c_str());
2600 ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
2601 ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
2602 ADDSTRING_TO_PAKET(series.episode.overview.c_str());
2603 resp->adddouble(series.episode.rating);
2604 resp->addULONG(series.episode.episodeImage.width);
2605 resp->addULONG(series.episode.episodeImage.height);
2607 ULONG num_actors = series.actors.size();
2608 resp->addULONG(num_actors);
2609 for (ULONG acty=0; acty < num_actors; acty++) {
2610 ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
2611 ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
2612 resp->addULONG(series.actors[acty].actorThumb.width);
2613 resp->addULONG(series.actors[acty].actorThumb.height);
2615 ULONG num_posters = series.posters.size();
2616 resp->addULONG(num_posters);
2617 for (ULONG medias = 0; medias < num_posters; medias++ ) {
2618 cTvMedia media=series.posters[medias];
2619 resp->addULONG(media.width);
2620 resp->addULONG(media.height);
2623 ULONG num_banners = series.banners.size();
2624 resp->addULONG(num_banners);
2625 for (ULONG medias = 0; medias < num_banners; medias++ ) {
2626 cTvMedia media=series.banners[medias];
2627 resp->addULONG(media.width);
2628 resp->addULONG(media.height);
2630 ULONG num_fanarts = series.fanarts.size();
2631 resp->addULONG(num_fanarts);
2632 for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
2633 cTvMedia media=series.fanarts[medias];
2634 resp->addULONG(media.width);
2635 resp->addULONG(media.height);
2637 resp->addULONG(series.seasonPoster.width);
2638 resp->addULONG(series.seasonPoster.height);
2642 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2647 int VompClientRRProc::processLoadTvMedia()
2649 TVMediaRequest tvreq;
2650 tvreq.streamID = req->requestID;
2651 tvreq.type = ntohl(*(ULONG*)req->data);
2652 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2653 tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
2654 tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
2655 tvreq.container = ntohl(*(ULONG*)(req->data+16));
2656 tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
2657 log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
2658 x.pict->addTVMediaRequest(tvreq);
2663 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2668 int VompClientRRProc::processLoadTvMediaRecThumb()
2670 TVMediaRequest tvreq;
2671 tvreq.streamID = req->requestID;
2672 tvreq.type = 3; // unknown but primary_name is set
2673 tvreq.primary_id = 0;
2674 tvreq.primary_name = std::string((const char*) req->data);
2675 tvreq.secondary_id = 0;
2676 tvreq.type_pict = 1;
2677 tvreq.container = 0;
2678 tvreq.container_member = 0;
2679 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2680 x.pict->addTVMediaRequest(tvreq);
2685 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2690 int VompClientRRProc::processLoadTvMediaEventThumb()
2692 TVMediaRequest tvreq;
2693 tvreq.streamID = req->requestID;
2694 tvreq.type = 4; // unknown but primary_id is set
2695 UINT channelid = ntohl(*(ULONG*)req->data);
2696 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2697 tvreq.secondary_id = 0;
2699 #if VDRVERSNUM >= 20301
2701 const cChannels* tChannels = Channels;
2703 cChannels* tChannels = &Channels;
2706 const cChannel* channel = tChannels->GetByNumber(channelid);
2708 if (channel) tvreq.primary_name = std::string((const char*)channel->GetChannelID().ToString());
2709 tvreq.type_pict = 1;
2710 tvreq.container = 0;
2711 tvreq.container_member = 0;
2712 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2713 x.pict->addTVMediaRequest(tvreq);
2718 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2723 int VompClientRRProc::processLoadChannelLogo()
2725 TVMediaRequest tvreq;
2726 tvreq.streamID = req->requestID;
2727 tvreq.type = 5; // channel logo
2728 UINT channelid = ntohl(*(ULONG*)req->data);
2729 tvreq.primary_id = channelid;
2730 tvreq.secondary_id = 0;
2732 #if VDRVERSNUM >= 20301
2734 const cChannels* tChannels = Channels;
2736 cChannels* tChannels = &Channels;
2739 const cChannel* channel = tChannels->GetByNumber(channelid);
2741 if (channel) tvreq.primary_name = std::string((const char*)channel->Name());
2742 tvreq.type_pict = 1;
2743 tvreq.container = 0;
2744 tvreq.container_member = 0;
2745 if (channel) log->log("RRProc", Log::DEBUG, "TVMedia request %d %d %s",req->requestID,channelid, channel->Name());
2746 x.pict->addTVMediaRequest(tvreq);
2751 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2759 #endif // !VOMPSTANDALONE