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());
889 int VompClientRRProc::processMoveRecording()
891 log->log("RRProc", Log::DEBUG, "Process move recording");
892 char* fileName = (char*)req->data;
893 char* newPath = NULL;
895 for (UINT k = 0; k < req->dataLength; k++)
897 if (req->data[k] == '\0')
899 newPath = (char*)&req->data[k+1];
903 if (!newPath) return 0;
906 #if VDRVERSNUM >= 20301
907 LOCK_RECORDINGS_WRITE;
908 cRecordings* tRecordings = Recordings;
910 cThreadLock RecordingsLock(&Recordings);
911 cRecordings* tRecordings = &Recordings;
914 cRecording* recording = tRecordings->GetByName((char*)fileName);
916 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
920 // TODO: Switch to using: int cRecording::IsInUse(void) const
921 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
924 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
925 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
926 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
928 const char* t = recording->FileName();
930 char* dateDirName = NULL; int k;
931 char* titleDirName = NULL; int j;
933 // Find the datedirname
934 for(k = strlen(t) - 1; k >= 0; k--)
938 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
939 dateDirName = new char[strlen(&t[k+1]) + 1];
940 strcpy(dateDirName, &t[k+1]);
945 // Find the titledirname
947 for(j = k-1; j >= 0; j--)
951 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
952 titleDirName = new char[(k - j - 1) + 1];
953 memcpy(titleDirName, &t[j+1], k - j - 1);
954 titleDirName[k - j - 1] = '\0';
959 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
960 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
961 #if APIVERSNUM > 20101
962 log->log("RRProc", Log::DEBUG, "viddir: %s", cVideoDirectory::Name());
964 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
967 char* newPathConv = (char*)malloc(strlen(newPath)+1);
968 strcpy(newPathConv, newPath);
969 newPathConv = ExchangeChars(newPathConv, true);
970 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
972 #if APIVERSNUM > 20101
973 char* newContainer = new char[strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1];
974 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1);
975 sprintf(newContainer, "%s%s%s", cVideoDirectory::Name(), newPathConv, titleDirName);
977 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
978 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
979 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
983 log->log("RRProc", Log::DEBUG, "%s", newContainer);
986 int statret = stat(newContainer, &dstat);
987 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
989 log->log("RRProc", Log::DEBUG, "new dir does not exist");
990 int mkdirret = mkdir(newContainer, 0755);
993 delete[] dateDirName;
994 delete[] titleDirName;
995 delete[] newContainer;
999 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1003 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
1005 delete[] dateDirName;
1006 delete[] titleDirName;
1007 delete[] newContainer;
1011 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1016 // Ok, the directory container has been made, or it pre-existed.
1018 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
1019 sprintf(newDir, "%s/%s", newContainer, dateDirName);
1021 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
1022 int renameret = rename(t, newDir);
1025 // Success. Test for remove old dir containter
1026 char* oldTitleDir = new char[k+1];
1027 memcpy(oldTitleDir, t, k);
1028 oldTitleDir[k] = '\0';
1029 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
1030 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
1031 delete[] oldTitleDir;
1033 #if VDRVERSNUM >= 20301
1034 tRecordings->SetModified();
1035 #elif VDRVERSNUM > 10311
1036 ::Recordings.Update();
1038 // Success. Send a different packet from just a ulong
1039 resp->addULONG(1); // success
1040 resp->addString(newDir); //system depent do not convert
1048 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1050 delete[] dateDirName;
1051 delete[] titleDirName;
1052 delete[] newContainer;
1059 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1066 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1072 int VompClientRRProc::processGetChannelsList()
1076 char* chanConfig = x.config.getValueString("General", "Channels");
1078 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
1080 #if VDRVERSNUM >= 20301
1082 const cChannels* tChannels = Channels;
1084 const cChannels* tChannels = &Channels;
1087 for (const cChannel *channel = tChannels->First(); channel; channel = tChannels->Next(channel))
1089 #if VDRVERSNUM < 10300
1090 if (!channel->GroupSep() && (!channel->Ca() || allChans))
1092 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
1095 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
1097 if (channel->Vpid()) type = 1;
1098 #if VDRVERSNUM < 10300
1101 else if (channel->Apid(0)) type = 2;
1105 resp->addULONG(channel->Number());
1106 resp->addULONG(type);
1107 resp->addString(x.charconvsys->Convert(channel->Name()));
1108 #if VDRVERSNUM < 10703
1111 resp->addULONG(channel->Vtype());
1117 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1119 log->log("RRProc", Log::DEBUG, "Written channels list");
1124 int VompClientRRProc::processGetChannelPids()
1126 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1128 #if VDRVERSNUM >= 20301
1130 const cChannels* tChannels = Channels;
1132 cChannels* tChannels = &Channels;
1135 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1140 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1149 #if VDRVERSNUM < 10300
1151 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
1152 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
1154 if (channel->Apid2())
1156 else if (channel->Apid1())
1163 for (const int *Apid = channel->Apids(); *Apid; Apid++)
1167 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
1171 for (const int *Spid = channel->Spids(); *Spid; Spid++)
1178 // Format of response
1197 resp->addULONG(channel->Vpid());
1198 #if VDRVERSNUM < 10703
1201 resp->addULONG(channel->Vtype());
1203 resp->addULONG(numApids);
1205 #if VDRVERSNUM < 10300
1208 resp->addULONG(channel->Apid1());
1209 resp->addString("");
1213 resp->addULONG(channel->Apid2());
1214 resp->addString("");
1219 for (ULONG i = 0; i < numApids; i++)
1221 resp->addULONG(channel->Apid(i));
1222 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1224 resp->addULONG(numDpids);
1225 for (ULONG i = 0; i < numDpids; i++)
1227 resp->addULONG(channel->Dpid(i));
1228 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1230 resp->addULONG(numSpids);
1231 for (ULONG i = 0; i < numSpids; i++)
1233 resp->addULONG(channel->Spid(i));
1234 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1237 resp->addULONG(channel->Tpid());
1238 // Format of extended response, for compatibility with older client at the end
1250 #if VDRVERSNUM < 10300
1260 for (ULONG i = 0; i < numApids; i++)
1262 #if VDRVERSNUM < 10715
1265 resp->addULONG(channel->Atype(i));
1268 for (ULONG i = 0; i < numDpids; i++)
1270 #if VDRVERSNUM < 10715
1271 resp->addULONG(0x6A /*AC3*/);
1273 resp->addULONG(channel->Dtype(i));
1276 for (ULONG i = 0; i < numSpids; i++)
1278 #if VDRVERSNUM < 10715
1283 resp->addULONG(channel->SubtitlingType(i));
1284 resp->addULONG(channel->CompositionPageId(i));
1285 resp->addULONG(channel->AncillaryPageId(i));
1292 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1294 log->log("RRProc", Log::DEBUG, "Written channels pids");
1299 int VompClientRRProc::processStartStreamingChannel()
1303 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1307 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1308 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1310 #if VDRVERSNUM >= 20301
1312 const cChannels* tChannels = Channels;
1314 cChannels* tChannels = &Channels;
1317 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1322 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1326 // get the priority we should use
1328 int priority = x.config.getValueLong("General", "Live priority", &fail);
1331 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1335 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1339 // a bit of sanity..
1340 #if VDRVERSNUM < 10725
1341 if (priority < 0) priority = 0;
1343 if (priority < -99) priority = -99;
1345 if (priority > 99) priority = 99;
1347 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1348 x.lp = MVPReceiver::create(channel, priority);
1354 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1358 if (!x.lp->init(&x.tcp, req->requestID))
1364 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1370 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1374 int VompClientRRProc::processStopStreaming()
1376 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1379 x.lp->detachMVPReceiver();
1383 else if (x.recplayer)
1385 x.writeResumeData();
1393 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1397 int VompClientRRProc::processGetBlock()
1401 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1407 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1411 UCHAR* data = req->data;
1413 ULLONG position = x.ntohll(*(ULLONG*)data);
1414 data += sizeof(ULLONG);
1415 ULONG amount = ntohl(*(ULONG*)data);
1417 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1419 UCHAR sendBuffer[amount];
1420 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1422 if (!amountReceived)
1425 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1429 resp->copyin(sendBuffer, amountReceived);
1430 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1434 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1435 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1439 int VompClientRRProc::processStartStreamingRecording()
1441 // data is a pointer to the fileName string
1443 #if VDRVERSNUM >= 20301
1444 LOCK_RECORDINGS_READ;
1445 const cRecordings* tRecordings = Recordings;
1447 cThreadLock RecordingsLock(&Recordings);
1448 cRecordings* tRecordings = &Recordings;
1451 const cRecording* recording = tRecordings->GetByName((char*)req->data);
1453 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1457 x.recplayer = new RecPlayer(recording);
1459 resp->addULLONG(x.recplayer->getLengthBytes());
1460 resp->addULONG(x.recplayer->getLengthFrames());
1462 #if VDRVERSNUM < 10703
1463 resp->addUCHAR(true);//added for TS
1465 resp->addUCHAR(recording->IsPesRecording());//added for TS
1469 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1471 log->log("RRProc", Log::DEBUG, "written totalLength");
1475 // No such recording exists
1478 resp->addUCHAR(false);
1480 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1481 log->log("RRProc", Log::DEBUG, "start streaming recording failed");
1487 int VompClientRRProc::processPositionFromFrameNumber()
1491 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1495 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1499 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1502 resp->addULLONG(retval);
1504 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1506 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1510 int VompClientRRProc::processFrameNumberFromPosition()
1514 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1518 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1522 retval = x.recplayer->frameNumberFromPosition(position);
1525 resp->addULONG(retval);
1527 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1529 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1533 int VompClientRRProc::processGetIFrame()
1535 bool success = false;
1537 ULONG* data = (ULONG*)req->data;
1539 ULONG frameNumber = ntohl(*data);
1541 ULONG direction = ntohl(*data);
1543 ULLONG rfilePosition = 0;
1544 ULONG rframeNumber = 0;
1545 ULONG rframeLength = 0;
1549 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1553 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1556 // returns file position, frame number, length
1560 resp->addULLONG(rfilePosition);
1561 resp->addULONG(rframeNumber);
1562 resp->addULONG(rframeLength);
1570 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1572 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1576 int VompClientRRProc::processGetChannelSchedule()
1578 ULONG* data = (ULONG*)req->data;
1580 ULONG channelNumber = ntohl(*data);
1582 ULONG startTime = ntohl(*data);
1584 ULONG duration = ntohl(*data);
1586 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1588 #if VDRVERSNUM >= 20301
1590 const cChannels* tChannels = Channels;
1592 cChannels* tChannels = &Channels;
1595 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1600 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1602 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1606 log->log("RRProc", Log::DEBUG, "Got channel");
1608 #if VDRVERSNUM < 10300
1609 cMutexLock MutexLock;
1610 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
1611 #elif VDRVERSNUM < 20301
1612 cSchedulesLock MutexLock;
1613 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
1615 LOCK_SCHEDULES_READ;
1616 const cSchedules *tSchedules = Schedules;
1623 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1625 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1629 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1631 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
1636 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1638 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1642 log->log("RRProc", Log::DEBUG, "Got schedule object");
1644 const char* empty = "";
1645 bool atLeastOneEvent = false;
1648 ULONG thisEventTime;
1649 ULONG thisEventDuration;
1650 const char* thisEventTitle;
1651 const char* thisEventSubTitle;
1652 const char* thisEventDescription;
1654 #if VDRVERSNUM < 10300
1656 const cEventInfo *event;
1657 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1659 event = Schedule->GetEventNumber(eventNumber);
1661 thisEventID = event->GetEventID();
1662 thisEventTime = event->GetTime();
1663 thisEventDuration = event->GetDuration();
1664 thisEventTitle = event->GetTitle();
1665 thisEventSubTitle = event->GetSubtitle();
1666 thisEventDescription = event->GetExtendedDescription();
1670 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1672 thisEventID = event->EventID();
1673 thisEventTime = event->StartTime();
1674 thisEventDuration = event->Duration();
1675 thisEventTitle = event->Title();
1676 thisEventSubTitle = NULL;
1677 thisEventDescription = event->Description();
1681 //in the past filter
1682 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1685 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1688 if (thisEventTime >= (startTime + duration)) continue;
1690 if (!thisEventTitle) thisEventTitle = empty;
1691 if (!thisEventSubTitle) thisEventSubTitle = empty;
1692 if (!thisEventDescription) thisEventDescription = empty;
1694 resp->addULONG(thisEventID);
1695 resp->addULONG(thisEventTime);
1696 resp->addULONG(thisEventDuration);
1698 resp->addString(x.charconvsys->Convert(thisEventTitle));
1699 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1700 resp->addString(x.charconvsys->Convert(thisEventDescription));
1702 atLeastOneEvent = true;
1705 log->log("RRProc", Log::DEBUG, "Got all event data");
1707 if (!atLeastOneEvent)
1710 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1714 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1716 log->log("RRProc", Log::DEBUG, "written schedules packet");
1721 int VompClientRRProc::processGetTimers()
1723 #if VDRVERSNUM >= 20301
1725 const cTimers* tTimers = Timers;
1727 const cTimers* tTimers = &Timers;
1730 const cTimer *timer;
1731 int numTimers = tTimers->Count();
1733 resp->addULONG(numTimers);
1735 for (int i = 0; i < numTimers; i++)
1737 timer = tTimers->Get(i);
1739 #if VDRVERSNUM < 10300
1740 resp->addULONG(timer->Active());
1742 resp->addULONG(timer->HasFlags(tfActive));
1744 resp->addULONG(timer->Recording());
1745 resp->addULONG(timer->Pending());
1746 resp->addULONG(timer->Priority());
1747 resp->addULONG(timer->Lifetime());
1748 resp->addULONG(timer->Channel()->Number());
1749 resp->addULONG(timer->StartTime());
1750 resp->addULONG(timer->StopTime());
1751 resp->addULONG(timer->Day());
1752 resp->addULONG(timer->WeekDays());
1753 resp->addString(timer->File()); //Filename is system specific and not visible by user
1757 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1759 log->log("RRProc", Log::DEBUG, "Written timers list");
1764 int VompClientRRProc::processSetTimer()
1766 char* timerString = new char[strlen((char*)req->data) + 1];
1767 strcpy(timerString, (char*)req->data);
1769 #if VDRVERSNUM < 10300
1771 // If this is VDR 1.2 the date part of the timer string must be reduced
1772 // to just DD rather than YYYY-MM-DD
1774 int s = 0; // source
1775 int d = 0; // destination
1777 while(c != 2) // copy up to date section, including the second ':'
1779 timerString[d] = req->data[s];
1780 if (req->data[s] == ':') c++;
1784 // now it has copied up to the date section
1786 while(c != 2) // waste YYYY-MM-
1788 if (req->data[s] == '-') c++;
1791 // now source is at the DD
1792 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1793 d += req->dataLength - s;
1794 timerString[d] = '\0';
1796 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1799 log->log("RRProc", Log::DEBUG, "%s", timerString);
1801 cTimer *timer = new cTimer;
1802 if (!timer->Parse((char*)timerString))
1806 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1811 #if VDRVERSNUM >= 20301
1813 cTimers* tTimers = Timers;
1815 cTimers* tTimers = &Timers;
1818 cTimer *t = tTimers->GetTimer(timer);
1823 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1828 timer->ClrFlags(tfRecording);
1829 tTimers->Add(timer);
1830 #if VDRVERSNUM < 10300
1832 #elif VDRVERSNUM < 20301
1833 tTimers->SetModified();
1838 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1842 int VompClientRRProc::processDeleteTimer()
1844 log->log("RRProc", Log::DEBUG, "Delete timer called");
1849 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1850 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1851 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1852 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1853 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1855 #if VDRVERSNUM >= 20301
1857 cTimers* tTimers = Timers;
1859 cTimers* tTimers = &Timers;
1863 for (ti = tTimers->First(); ti; ti = tTimers->Next(ti))
1865 if ( (ti->Channel()->Number() == delChannel)
1866 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1867 && (ti->StartTime() == delStart)
1868 && (ti->StopTime() == delStop) )
1876 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1880 #if VDRVERSNUM < 20301
1881 // I suppose with the new locking this just can't happen
1882 if (tTimers->BeingEdited())
1884 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1887 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1892 if (ti->Recording())
1894 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1897 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1902 tTimers->SetModified();
1906 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1910 int VompClientRRProc::processGetRecInfo()
1912 // data is a pointer to the fileName string
1913 #if VDRVERSNUM >= 20301
1914 LOCK_RECORDINGS_READ;
1915 const cRecordings* tRecordings = Recordings;
1917 cThreadLock RecordingsLock(&Recordings);
1918 cRecordings* tRecordings = &Recordings;
1921 const cRecording *recording = tRecordings->GetByName((char*)req->data);
1923 time_t timerStart = 0;
1924 time_t timerStop = 0;
1925 char* summary = NULL;
1926 char* shorttext = NULL;
1927 char* description = NULL;
1929 bool newsummary=false;
1930 ULONG resumePoint = 0;
1934 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1937 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1942 4 bytes: start time for timer
1943 4 bytes: end time for timer
1944 4 bytes: resume point
1946 4 bytes: num components
1953 8 bytes: frames per second
1957 // Get current timer
1959 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1962 timerStart = rc->Timer()->StartTime();
1963 timerStop = rc->Timer()->StopTime();
1964 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1967 resp->addULONG(timerStart);
1968 resp->addULONG(timerStop);
1972 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1975 resumePoint = strtoul(value, NULL, 10);
1979 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1982 ResumeId = atoi(ResumeIdC);
1986 ResumeId = 0; //default if not defined in vomp-MAC.conf
1988 while (ResumeIDLock)
1989 cCondWait::SleepMs(100);
1990 ResumeIDLock = true;
1991 int OldSetupResumeID = Setup.ResumeID;
1992 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1993 #if VDRVERSNUM < 10703
1994 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1996 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1998 Setup.ResumeID = OldSetupResumeID; //and restore it back
1999 ResumeIDLock = false;
2001 int resume = ResumeFile.Read();
2002 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2004 resumePoint = ResumeFile.Read();
2006 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2008 resp->addULONG(resumePoint);
2012 #if VDRVERSNUM < 10300
2013 summary = (char*)recording->Summary();
2015 const cRecordingInfo *Info = recording->Info();
2016 shorttext = (char*)Info->ShortText();
2017 description = (char*) (char*)Info->Description();
2018 if (isempty(shorttext)) summary=description;
2019 else if (isempty(description)) summary=shorttext;
2021 int length=strlen(description)+strlen(shorttext)+4;
2022 summary=new char[length];
2023 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2027 if (isempty(summary)) summary = (char*)Info->Description();
2029 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2032 resp->addString(x.charconvsys->Convert(summary));
2033 if (newsummary) delete [] summary;
2037 resp->addString("");
2042 #if VDRVERSNUM < 10300
2044 // Send 0 for numchannels - this signals the client this info is not available
2048 const cComponents* components = Info->Components();
2050 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2058 resp->addULONG(components->NumComponents());
2060 tComponent* component;
2061 for (int i = 0; i < components->NumComponents(); i++)
2063 component = components->Component(i);
2065 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2067 resp->addUCHAR(component->stream);
2068 resp->addUCHAR(component->type);
2070 if (component->language)
2072 resp->addString(x.charconvsys->Convert(component->language));
2076 resp->addString("");
2078 if (component->description)
2080 resp->addString(x.charconvsys->Convert(component->description));
2084 resp->addString("");
2090 double framespersec;
2091 #if VDRVERSNUM < 10703
2092 framespersec = FRAMESPERSEC;
2094 framespersec = Info->FramesPerSecond();
2096 resp->adddouble(framespersec);
2097 title = (char*)Info->Title();
2100 resp->addString(x.charconvsys->Convert(title));
2104 resp->addString(x.charconvsys->Convert(recording->Name()));
2110 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2112 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2117 // TODO Remove getrecinfo(1) for version 0.6.0 and rename getrecinfo2 to getrecinfo
2119 int VompClientRRProc::processGetRecInfo2()
2121 // data is a pointer to the fileName string
2122 #if VDRVERSNUM >= 20301
2123 LOCK_RECORDINGS_READ;
2124 const cRecordings* tRecordings = Recordings;
2126 cThreadLock RecordingsLock(&Recordings);
2127 cRecordings* tRecordings = &Recordings;
2130 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2132 time_t timerStart = 0;
2133 time_t timerStop = 0;
2134 char* summary = NULL;
2135 char* shorttext = NULL;
2136 char* description = NULL;
2138 bool newsummary=false;
2139 ULONG resumePoint = 0;
2143 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
2146 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2151 4 bytes: start time for timer
2152 4 bytes: end time for timer
2153 4 bytes: resume point
2155 4 bytes: num components
2162 8 bytes: frames per second
2164 // new stuff starts here
2165 string: channel name
2172 // Get current timer
2174 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
2177 timerStart = rc->Timer()->StartTime();
2178 timerStop = rc->Timer()->StopTime();
2179 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
2182 resp->addULONG(timerStart);
2183 resp->addULONG(timerStop);
2187 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
2190 resumePoint = strtoul(value, NULL, 10);
2194 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
2197 ResumeId = atoi(ResumeIdC);
2201 ResumeId = 0; //default if not defined in vomp-MAC.conf
2203 while (ResumeIDLock)
2204 cCondWait::SleepMs(100);
2205 ResumeIDLock = true;
2206 int OldSetupResumeID = Setup.ResumeID;
2207 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
2208 #if VDRVERSNUM < 10703
2209 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
2211 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
2213 Setup.ResumeID = OldSetupResumeID; //and restore it back
2214 ResumeIDLock = false;
2216 int resume = ResumeFile.Read();
2217 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2219 resumePoint = ResumeFile.Read();
2221 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2223 resp->addULONG(resumePoint);
2227 #if VDRVERSNUM < 10300
2228 summary = (char*)recording->Summary();
2230 const cRecordingInfo *Info = recording->Info();
2231 shorttext = (char*)Info->ShortText();
2232 description = (char*) (char*)Info->Description();
2233 if (isempty(shorttext)) summary=description;
2234 else if (isempty(description)) summary=shorttext;
2236 int length=strlen(description)+strlen(shorttext)+4;
2237 summary=new char[length];
2238 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2242 if (isempty(summary)) summary = (char*)Info->Description();
2244 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2247 resp->addString(x.charconvsys->Convert(summary));
2248 if (newsummary) delete [] summary;
2252 resp->addString("");
2257 #if VDRVERSNUM < 10300
2259 // Send 0 for numchannels - this signals the client this info is not available
2263 const cComponents* components = Info->Components();
2265 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2273 resp->addULONG(components->NumComponents());
2275 tComponent* component;
2276 for (int i = 0; i < components->NumComponents(); i++)
2278 component = components->Component(i);
2280 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2282 resp->addUCHAR(component->stream);
2283 resp->addUCHAR(component->type);
2285 if (component->language)
2287 resp->addString(x.charconvsys->Convert(component->language));
2291 resp->addString("");
2293 if (component->description)
2295 resp->addString(x.charconvsys->Convert(component->description));
2299 resp->addString("");
2305 double framespersec;
2306 #if VDRVERSNUM < 10703
2307 framespersec = FRAMESPERSEC;
2309 framespersec = Info->FramesPerSecond();
2311 resp->adddouble(framespersec);
2312 title = (char*)Info->Title();
2315 resp->addString(x.charconvsys->Convert(title));
2319 resp->addString(x.charconvsys->Convert(recording->Name()));
2323 if (Info->ChannelName())
2325 resp->addString(x.charconvsys->Convert(Info->ChannelName()));
2329 resp->addString("");
2332 resp->addULONG(recording->LengthInSeconds());
2333 resp->addULONG(recording->FileSizeMB());
2334 resp->addULONG(recording->Priority());
2335 resp->addULONG(recording->Lifetime());
2340 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2342 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2349 int VompClientRRProc::processReScanRecording()
2353 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
2357 x.recplayer->scan();
2359 resp->addULLONG(x.recplayer->getLengthBytes());
2360 resp->addULONG(x.recplayer->getLengthFrames());
2362 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2363 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
2367 // FIXME without client calling rescan, getblock wont work even tho more data is avail
2369 int VompClientRRProc::processGetMarks()
2371 // data is a pointer to the fileName string
2372 #if VDRVERSNUM >= 20301
2373 LOCK_RECORDINGS_READ;
2374 const cRecordings* tRecordings = Recordings;
2376 cThreadLock RecordingsLock(&Recordings);
2377 cRecordings* tRecordings = &Recordings;
2380 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2381 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
2386 #if VDRVERSNUM < 10703
2387 Marks.Load(recording->FileName());
2389 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
2393 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
2395 #if VDRVERSNUM < 10721
2396 ULLONG mposition = m->position;
2398 ULLONG mposition = m->Position();
2400 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
2402 resp->addULONG(mposition);
2407 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
2413 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2415 log->log("RRProc", Log::DEBUG, "Written Marks list");
2420 int VompClientRRProc::processVDRShutdown()
2422 log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
2423 VompClient::decClients(); // Temporarily make this client disappear
2424 cRemote::Put(kPower);
2425 VompClient::incClients();
2427 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2431 int VompClientRRProc::processGetRecScraperEventType()
2433 #if VDRVERSNUM >= 20301
2434 LOCK_RECORDINGS_READ;
2435 const cRecordings* tRecordings = Recordings;
2437 cThreadLock RecordingsLock(&Recordings);
2438 cRecordings* tRecordings = &Recordings;
2441 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2442 ScraperGetEventType call;
2445 if (recording && x.scrapQuery())
2447 call.recording = recording;
2448 x.scraper->Service("GetEventType", &call);
2450 resp->addUCHAR(call.type);
2451 if (call.type == tMovie)
2453 resp->addLONG(call.movieId);
2455 else if (call.type == tSeries)
2457 resp->addLONG(call.seriesId);
2458 resp->addLONG(call.episodeId);
2461 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2466 int VompClientRRProc::processGetEventScraperEventType()
2468 ScraperGetEventType call;
2470 ULONG channelid = ntohl(*(ULONG*)req->data);
2471 ULONG eventid = ntohl(*(ULONG*)(req->data+4));
2472 const cEvent *event = NULL;
2474 #if VDRVERSNUM >= 20301
2476 const cChannels* tChannels = Channels;
2478 cChannels* tChannels = &Channels;
2481 const cChannel* channel = tChannels->GetByNumber(channelid);
2483 #if VDRVERSNUM < 10300
2484 cMutexLock MutexLock;
2485 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
2486 #elif VDRVERSNUM < 20301
2487 cSchedulesLock MutexLock;
2488 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
2490 LOCK_SCHEDULES_READ;
2491 const cSchedules *tSchedules = Schedules;
2494 if (tSchedules && channel)
2496 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
2498 event = Schedule->GetEvent(eventid);
2502 if (event && x.scrapQuery())
2505 x.scraper->Service("GetEventType",&call);
2507 resp->addUCHAR(call.type);
2508 if (call.type == tMovie)
2510 resp->addLONG(call.movieId);
2511 } else if (call.type == tSeries){
2512 resp->addLONG(call.seriesId);
2513 resp->addLONG(call.episodeId);
2515 if (x.pict->epgImageExists(eventid)) {
2522 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2527 #define ADDSTRING_TO_PAKET(y) if ((y)!=0) resp->addString(x.charconvutf8->Convert(y)); else resp->addString("");
2529 int VompClientRRProc::processGetScraperMovieInfo()
2533 movie.movieId = ntohl(*(ULONG*)req->data);
2534 if (!x.scrapQuery()) {
2535 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2536 return 0; //stupid, I have no scraper why are you still asking
2538 x.scraper->Service("GetMovie",&movie);
2541 ADDSTRING_TO_PAKET(movie.title.c_str());
2542 ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
2543 ADDSTRING_TO_PAKET(movie.tagline.c_str());
2544 ADDSTRING_TO_PAKET(movie.overview.c_str());
2545 resp->addUCHAR(movie.adult);
2546 ADDSTRING_TO_PAKET(movie.collectionName.c_str());
2548 resp->addLONG(movie.budget);
2549 resp->addLONG(movie.revenue);
2550 ADDSTRING_TO_PAKET(movie.genres.c_str());
2551 ADDSTRING_TO_PAKET(movie.homepage.c_str());
2552 ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
2553 resp->addLONG(movie.runtime);
2554 resp->adddouble(movie.popularity);
2555 resp->adddouble(movie.voteAverage);
2556 resp->addULONG(movie.poster.width);
2557 resp->addULONG(movie.poster.height);
2558 resp->addULONG(movie.fanart.width);
2559 resp->addULONG(movie.fanart.height);
2560 resp->addULONG(movie.collectionPoster.width);
2561 resp->addULONG(movie.collectionPoster.height);
2562 resp->addULONG(movie.collectionFanart.width);
2563 resp->addULONG(movie.collectionFanart.height);
2564 resp->addULONG(movie.actors.size());
2565 for (ULONG acty=0; acty < movie.actors.size(); acty++) {
2566 ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
2567 ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
2568 resp->addULONG(movie.actors[acty].actorThumb.width);
2569 resp->addULONG(movie.actors[acty].actorThumb.height);
2573 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2580 int VompClientRRProc::processGetScraperSeriesInfo()
2583 series.seriesId = ntohl(*(ULONG*)req->data);
2584 series.episodeId = ntohl(*(ULONG*)(req->data+4));
2585 if (!x.scrapQuery()) {
2586 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2587 return 0; //stupid, I have no scraper why are you still asking
2589 x.scraper->Service("GetSeries",&series);
2591 ADDSTRING_TO_PAKET(series.name.c_str());
2592 ADDSTRING_TO_PAKET(series.overview.c_str());
2593 ADDSTRING_TO_PAKET(series.firstAired.c_str());
2594 ADDSTRING_TO_PAKET(series.network.c_str());
2595 ADDSTRING_TO_PAKET(series.genre.c_str());
2596 resp->adddouble(series.rating);
2597 ADDSTRING_TO_PAKET(series.status.c_str());
2599 resp->addLONG(series.episode.number);
2600 resp->addLONG(series.episode.season);
2601 ADDSTRING_TO_PAKET(series.episode.name.c_str());
2602 ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
2603 ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
2604 ADDSTRING_TO_PAKET(series.episode.overview.c_str());
2605 resp->adddouble(series.episode.rating);
2606 resp->addULONG(series.episode.episodeImage.width);
2607 resp->addULONG(series.episode.episodeImage.height);
2609 ULONG num_actors = series.actors.size();
2610 resp->addULONG(num_actors);
2611 for (ULONG acty=0; acty < num_actors; acty++) {
2612 ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
2613 ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
2614 resp->addULONG(series.actors[acty].actorThumb.width);
2615 resp->addULONG(series.actors[acty].actorThumb.height);
2617 ULONG num_posters = series.posters.size();
2618 resp->addULONG(num_posters);
2619 for (ULONG medias = 0; medias < num_posters; medias++ ) {
2620 cTvMedia media=series.posters[medias];
2621 resp->addULONG(media.width);
2622 resp->addULONG(media.height);
2625 ULONG num_banners = series.banners.size();
2626 resp->addULONG(num_banners);
2627 for (ULONG medias = 0; medias < num_banners; medias++ ) {
2628 cTvMedia media=series.banners[medias];
2629 resp->addULONG(media.width);
2630 resp->addULONG(media.height);
2632 ULONG num_fanarts = series.fanarts.size();
2633 resp->addULONG(num_fanarts);
2634 for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
2635 cTvMedia media=series.fanarts[medias];
2636 resp->addULONG(media.width);
2637 resp->addULONG(media.height);
2639 resp->addULONG(series.seasonPoster.width);
2640 resp->addULONG(series.seasonPoster.height);
2644 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2649 int VompClientRRProc::processLoadTvMedia()
2651 TVMediaRequest tvreq;
2652 tvreq.streamID = req->requestID;
2653 tvreq.type = ntohl(*(ULONG*)req->data);
2654 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2655 tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
2656 tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
2657 tvreq.container = ntohl(*(ULONG*)(req->data+16));
2658 tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
2659 log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
2660 x.pict->addTVMediaRequest(tvreq);
2665 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2670 int VompClientRRProc::processLoadTvMediaRecThumb()
2672 TVMediaRequest tvreq;
2673 tvreq.streamID = req->requestID;
2674 tvreq.type = 3; // unknown but primary_name is set
2675 tvreq.primary_id = 0;
2676 tvreq.primary_name = std::string((const char*) req->data);
2677 tvreq.secondary_id = 0;
2678 tvreq.type_pict = 1;
2679 tvreq.container = 0;
2680 tvreq.container_member = 0;
2681 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2682 x.pict->addTVMediaRequest(tvreq);
2687 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2692 int VompClientRRProc::processLoadTvMediaEventThumb()
2694 TVMediaRequest tvreq;
2695 tvreq.streamID = req->requestID;
2696 tvreq.type = 4; // unknown but primary_id is set
2697 UINT channelid = ntohl(*(ULONG*)req->data);
2698 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2699 tvreq.secondary_id = 0;
2701 #if VDRVERSNUM >= 20301
2703 const cChannels* tChannels = Channels;
2705 cChannels* tChannels = &Channels;
2708 const cChannel* channel = tChannels->GetByNumber(channelid);
2710 if (channel) tvreq.primary_name = std::string((const char*)channel->GetChannelID().ToString());
2711 tvreq.type_pict = 1;
2712 tvreq.container = 0;
2713 tvreq.container_member = 0;
2714 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2715 x.pict->addTVMediaRequest(tvreq);
2720 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2725 int VompClientRRProc::processLoadChannelLogo()
2727 TVMediaRequest tvreq;
2728 tvreq.streamID = req->requestID;
2729 tvreq.type = 5; // channel logo
2730 UINT channelid = ntohl(*(ULONG*)req->data);
2731 tvreq.primary_id = channelid;
2732 tvreq.secondary_id = 0;
2734 #if VDRVERSNUM >= 20301
2736 const cChannels* tChannels = Channels;
2738 cChannels* tChannels = &Channels;
2741 const cChannel* channel = tChannels->GetByNumber(channelid);
2743 if (channel) tvreq.primary_name = std::string((const char*)channel->Name());
2744 tvreq.type_pict = 1;
2745 tvreq.container = 0;
2746 tvreq.container_member = 0;
2747 if (channel) log->log("RRProc", Log::DEBUG, "TVMedia request %d %d %s",req->requestID,channelid, channel->Name());
2748 x.pict->addTVMediaRequest(tvreq);
2753 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2761 #endif // !VOMPSTANDALONE