2 Copyright 2008 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");
1475 int VompClientRRProc::processPositionFromFrameNumber()
1479 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1483 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1487 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1490 resp->addULLONG(retval);
1492 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1494 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1498 int VompClientRRProc::processFrameNumberFromPosition()
1502 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1506 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1510 retval = x.recplayer->frameNumberFromPosition(position);
1513 resp->addULONG(retval);
1515 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1517 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1521 int VompClientRRProc::processGetIFrame()
1523 bool success = false;
1525 ULONG* data = (ULONG*)req->data;
1527 ULONG frameNumber = ntohl(*data);
1529 ULONG direction = ntohl(*data);
1531 ULLONG rfilePosition = 0;
1532 ULONG rframeNumber = 0;
1533 ULONG rframeLength = 0;
1537 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1541 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1544 // returns file position, frame number, length
1548 resp->addULLONG(rfilePosition);
1549 resp->addULONG(rframeNumber);
1550 resp->addULONG(rframeLength);
1558 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1560 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1564 int VompClientRRProc::processGetChannelSchedule()
1566 ULONG* data = (ULONG*)req->data;
1568 ULONG channelNumber = ntohl(*data);
1570 ULONG startTime = ntohl(*data);
1572 ULONG duration = ntohl(*data);
1574 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1576 #if VDRVERSNUM >= 20301
1578 const cChannels* tChannels = Channels;
1580 cChannels* tChannels = &Channels;
1583 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1588 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1590 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1594 log->log("RRProc", Log::DEBUG, "Got channel");
1596 #if VDRVERSNUM < 10300
1597 cMutexLock MutexLock;
1598 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
1599 #elif VDRVERSNUM < 20301
1600 cSchedulesLock MutexLock;
1601 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
1603 LOCK_SCHEDULES_READ;
1604 const cSchedules *tSchedules = Schedules;
1611 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1613 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1617 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1619 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
1624 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1626 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1630 log->log("RRProc", Log::DEBUG, "Got schedule object");
1632 const char* empty = "";
1633 bool atLeastOneEvent = false;
1636 ULONG thisEventTime;
1637 ULONG thisEventDuration;
1638 const char* thisEventTitle;
1639 const char* thisEventSubTitle;
1640 const char* thisEventDescription;
1642 #if VDRVERSNUM < 10300
1644 const cEventInfo *event;
1645 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1647 event = Schedule->GetEventNumber(eventNumber);
1649 thisEventID = event->GetEventID();
1650 thisEventTime = event->GetTime();
1651 thisEventDuration = event->GetDuration();
1652 thisEventTitle = event->GetTitle();
1653 thisEventSubTitle = event->GetSubtitle();
1654 thisEventDescription = event->GetExtendedDescription();
1658 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1660 thisEventID = event->EventID();
1661 thisEventTime = event->StartTime();
1662 thisEventDuration = event->Duration();
1663 thisEventTitle = event->Title();
1664 thisEventSubTitle = NULL;
1665 thisEventDescription = event->Description();
1669 //in the past filter
1670 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1673 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1676 if (thisEventTime >= (startTime + duration)) continue;
1678 if (!thisEventTitle) thisEventTitle = empty;
1679 if (!thisEventSubTitle) thisEventSubTitle = empty;
1680 if (!thisEventDescription) thisEventDescription = empty;
1682 resp->addULONG(thisEventID);
1683 resp->addULONG(thisEventTime);
1684 resp->addULONG(thisEventDuration);
1686 resp->addString(x.charconvsys->Convert(thisEventTitle));
1687 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1688 resp->addString(x.charconvsys->Convert(thisEventDescription));
1690 atLeastOneEvent = true;
1693 log->log("RRProc", Log::DEBUG, "Got all event data");
1695 if (!atLeastOneEvent)
1698 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1702 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1704 log->log("RRProc", Log::DEBUG, "written schedules packet");
1709 int VompClientRRProc::processGetTimers()
1711 #if VDRVERSNUM >= 20301
1713 const cTimers* tTimers = Timers;
1715 const cTimers* tTimers = &Timers;
1718 const cTimer *timer;
1719 int numTimers = tTimers->Count();
1721 resp->addULONG(numTimers);
1723 for (int i = 0; i < numTimers; i++)
1725 timer = tTimers->Get(i);
1727 #if VDRVERSNUM < 10300
1728 resp->addULONG(timer->Active());
1730 resp->addULONG(timer->HasFlags(tfActive));
1732 resp->addULONG(timer->Recording());
1733 resp->addULONG(timer->Pending());
1734 resp->addULONG(timer->Priority());
1735 resp->addULONG(timer->Lifetime());
1736 resp->addULONG(timer->Channel()->Number());
1737 resp->addULONG(timer->StartTime());
1738 resp->addULONG(timer->StopTime());
1739 resp->addULONG(timer->Day());
1740 resp->addULONG(timer->WeekDays());
1741 resp->addString(timer->File()); //Filename is system specific and not visible by user
1745 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1747 log->log("RRProc", Log::DEBUG, "Written timers list");
1752 int VompClientRRProc::processSetTimer()
1754 char* timerString = new char[strlen((char*)req->data) + 1];
1755 strcpy(timerString, (char*)req->data);
1757 #if VDRVERSNUM < 10300
1759 // If this is VDR 1.2 the date part of the timer string must be reduced
1760 // to just DD rather than YYYY-MM-DD
1762 int s = 0; // source
1763 int d = 0; // destination
1765 while(c != 2) // copy up to date section, including the second ':'
1767 timerString[d] = req->data[s];
1768 if (req->data[s] == ':') c++;
1772 // now it has copied up to the date section
1774 while(c != 2) // waste YYYY-MM-
1776 if (req->data[s] == '-') c++;
1779 // now source is at the DD
1780 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1781 d += req->dataLength - s;
1782 timerString[d] = '\0';
1784 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1787 log->log("RRProc", Log::DEBUG, "%s", timerString);
1789 cTimer *timer = new cTimer;
1790 if (!timer->Parse((char*)timerString))
1794 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1799 #if VDRVERSNUM >= 20301
1801 cTimers* tTimers = Timers;
1803 cTimers* tTimers = &Timers;
1806 cTimer *t = tTimers->GetTimer(timer);
1811 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1816 timer->ClrFlags(tfRecording);
1817 tTimers->Add(timer);
1818 #if VDRVERSNUM < 10300
1820 #elif VDRVERSNUM < 20301
1821 tTimers->SetModified();
1826 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1830 int VompClientRRProc::processDeleteTimer()
1832 log->log("RRProc", Log::DEBUG, "Delete timer called");
1837 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1838 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1839 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1840 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1841 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1843 #if VDRVERSNUM >= 20301
1845 cTimers* tTimers = Timers;
1847 cTimers* tTimers = &Timers;
1851 for (ti = tTimers->First(); ti; ti = tTimers->Next(ti))
1853 if ( (ti->Channel()->Number() == delChannel)
1854 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1855 && (ti->StartTime() == delStart)
1856 && (ti->StopTime() == delStop) )
1864 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1868 #if VDRVERSNUM < 20301
1869 // I suppose with the new locking this just can't happen
1870 if (tTimers->BeingEdited())
1872 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1875 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1880 if (ti->Recording())
1882 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1885 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1890 tTimers->SetModified();
1894 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1898 int VompClientRRProc::processGetRecInfo()
1900 // data is a pointer to the fileName string
1901 #if VDRVERSNUM >= 20301
1902 LOCK_RECORDINGS_READ;
1903 const cRecordings* tRecordings = Recordings;
1905 cThreadLock RecordingsLock(&Recordings);
1906 cRecordings* tRecordings = &Recordings;
1909 const cRecording *recording = tRecordings->GetByName((char*)req->data);
1911 time_t timerStart = 0;
1912 time_t timerStop = 0;
1913 char* summary = NULL;
1914 char* shorttext = NULL;
1915 char* description = NULL;
1917 bool newsummary=false;
1918 ULONG resumePoint = 0;
1922 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1925 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1930 4 bytes: start time for timer
1931 4 bytes: end time for timer
1932 4 bytes: resume point
1934 4 bytes: num components
1941 8 bytes: frames per second
1945 // Get current timer
1947 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1950 timerStart = rc->Timer()->StartTime();
1951 timerStop = rc->Timer()->StopTime();
1952 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1955 resp->addULONG(timerStart);
1956 resp->addULONG(timerStop);
1960 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1963 resumePoint = strtoul(value, NULL, 10);
1967 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1970 ResumeId = atoi(ResumeIdC);
1974 ResumeId = 0; //default if not defined in vomp-MAC.conf
1976 while (ResumeIDLock)
1977 cCondWait::SleepMs(100);
1978 ResumeIDLock = true;
1979 int OldSetupResumeID = Setup.ResumeID;
1980 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1981 #if VDRVERSNUM < 10703
1982 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1984 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1986 Setup.ResumeID = OldSetupResumeID; //and restore it back
1987 ResumeIDLock = false;
1989 int resume = ResumeFile.Read();
1990 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1992 resumePoint = ResumeFile.Read();
1994 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1996 resp->addULONG(resumePoint);
2000 #if VDRVERSNUM < 10300
2001 summary = (char*)recording->Summary();
2003 const cRecordingInfo *Info = recording->Info();
2004 shorttext = (char*)Info->ShortText();
2005 description = (char*) (char*)Info->Description();
2006 if (isempty(shorttext)) summary=description;
2007 else if (isempty(description)) summary=shorttext;
2009 int length=strlen(description)+strlen(shorttext)+4;
2010 summary=new char[length];
2011 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2015 if (isempty(summary)) summary = (char*)Info->Description();
2017 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2020 resp->addString(x.charconvsys->Convert(summary));
2021 if (newsummary) delete [] summary;
2025 resp->addString("");
2030 #if VDRVERSNUM < 10300
2032 // Send 0 for numchannels - this signals the client this info is not available
2036 const cComponents* components = Info->Components();
2038 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2046 resp->addULONG(components->NumComponents());
2048 tComponent* component;
2049 for (int i = 0; i < components->NumComponents(); i++)
2051 component = components->Component(i);
2053 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2055 resp->addUCHAR(component->stream);
2056 resp->addUCHAR(component->type);
2058 if (component->language)
2060 resp->addString(x.charconvsys->Convert(component->language));
2064 resp->addString("");
2066 if (component->description)
2068 resp->addString(x.charconvsys->Convert(component->description));
2072 resp->addString("");
2078 double framespersec;
2079 #if VDRVERSNUM < 10703
2080 framespersec = FRAMESPERSEC;
2082 framespersec = Info->FramesPerSecond();
2084 resp->adddouble(framespersec);
2085 title = (char*)Info->Title();
2088 resp->addString(x.charconvsys->Convert(title));
2092 resp->addString(x.charconvsys->Convert(recording->Name()));
2098 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2100 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2105 // TODO Remove getrecinfo(1) for version 0.6.0 and rename getrecinfo2 to getrecinfo
2107 int VompClientRRProc::processGetRecInfo2()
2109 // data is a pointer to the fileName string
2110 #if VDRVERSNUM >= 20301
2111 LOCK_RECORDINGS_READ;
2112 const cRecordings* tRecordings = Recordings;
2114 cThreadLock RecordingsLock(&Recordings);
2115 cRecordings* tRecordings = &Recordings;
2118 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2120 time_t timerStart = 0;
2121 time_t timerStop = 0;
2122 char* summary = NULL;
2123 char* shorttext = NULL;
2124 char* description = NULL;
2126 bool newsummary=false;
2127 ULONG resumePoint = 0;
2131 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
2134 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2139 4 bytes: start time for timer
2140 4 bytes: end time for timer
2141 4 bytes: resume point
2143 4 bytes: num components
2150 8 bytes: frames per second
2152 // new stuff starts here
2153 string: channel name
2160 // Get current timer
2162 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
2165 timerStart = rc->Timer()->StartTime();
2166 timerStop = rc->Timer()->StopTime();
2167 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
2170 resp->addULONG(timerStart);
2171 resp->addULONG(timerStop);
2175 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
2178 resumePoint = strtoul(value, NULL, 10);
2182 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
2185 ResumeId = atoi(ResumeIdC);
2189 ResumeId = 0; //default if not defined in vomp-MAC.conf
2191 while (ResumeIDLock)
2192 cCondWait::SleepMs(100);
2193 ResumeIDLock = true;
2194 int OldSetupResumeID = Setup.ResumeID;
2195 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
2196 #if VDRVERSNUM < 10703
2197 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
2199 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
2201 Setup.ResumeID = OldSetupResumeID; //and restore it back
2202 ResumeIDLock = false;
2204 int resume = ResumeFile.Read();
2205 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2207 resumePoint = ResumeFile.Read();
2209 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2211 resp->addULONG(resumePoint);
2215 #if VDRVERSNUM < 10300
2216 summary = (char*)recording->Summary();
2218 const cRecordingInfo *Info = recording->Info();
2219 shorttext = (char*)Info->ShortText();
2220 description = (char*) (char*)Info->Description();
2221 if (isempty(shorttext)) summary=description;
2222 else if (isempty(description)) summary=shorttext;
2224 int length=strlen(description)+strlen(shorttext)+4;
2225 summary=new char[length];
2226 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2230 if (isempty(summary)) summary = (char*)Info->Description();
2232 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2235 resp->addString(x.charconvsys->Convert(summary));
2236 if (newsummary) delete [] summary;
2240 resp->addString("");
2245 #if VDRVERSNUM < 10300
2247 // Send 0 for numchannels - this signals the client this info is not available
2251 const cComponents* components = Info->Components();
2253 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2261 resp->addULONG(components->NumComponents());
2263 tComponent* component;
2264 for (int i = 0; i < components->NumComponents(); i++)
2266 component = components->Component(i);
2268 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2270 resp->addUCHAR(component->stream);
2271 resp->addUCHAR(component->type);
2273 if (component->language)
2275 resp->addString(x.charconvsys->Convert(component->language));
2279 resp->addString("");
2281 if (component->description)
2283 resp->addString(x.charconvsys->Convert(component->description));
2287 resp->addString("");
2293 double framespersec;
2294 #if VDRVERSNUM < 10703
2295 framespersec = FRAMESPERSEC;
2297 framespersec = Info->FramesPerSecond();
2299 resp->adddouble(framespersec);
2300 title = (char*)Info->Title();
2303 resp->addString(x.charconvsys->Convert(title));
2307 resp->addString(x.charconvsys->Convert(recording->Name()));
2311 if (Info->ChannelName())
2313 resp->addString(x.charconvsys->Convert(Info->ChannelName()));
2317 resp->addString("");
2320 resp->addULONG(recording->LengthInSeconds());
2321 resp->addULONG(recording->FileSizeMB());
2322 resp->addULONG(recording->Priority());
2323 resp->addULONG(recording->Lifetime());
2328 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2330 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2337 int VompClientRRProc::processReScanRecording()
2341 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
2345 x.recplayer->scan();
2347 resp->addULLONG(x.recplayer->getLengthBytes());
2348 resp->addULONG(x.recplayer->getLengthFrames());
2350 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2351 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
2355 // FIXME without client calling rescan, getblock wont work even tho more data is avail
2357 int VompClientRRProc::processGetMarks()
2359 // data is a pointer to the fileName string
2360 #if VDRVERSNUM >= 20301
2361 LOCK_RECORDINGS_READ;
2362 const cRecordings* tRecordings = Recordings;
2364 cThreadLock RecordingsLock(&Recordings);
2365 cRecordings* tRecordings = &Recordings;
2368 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2369 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
2374 #if VDRVERSNUM < 10703
2375 Marks.Load(recording->FileName());
2377 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
2381 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
2383 #if VDRVERSNUM < 10721
2384 ULLONG mposition = m->position;
2386 ULLONG mposition = m->Position();
2388 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
2390 resp->addULONG(mposition);
2395 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
2401 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2403 log->log("RRProc", Log::DEBUG, "Written Marks list");
2408 int VompClientRRProc::processVDRShutdown()
2410 log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
2411 VompClient::decClients(); // Temporarily make this client disappear
2412 cRemote::Put(kPower);
2413 VompClient::incClients();
2415 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2419 int VompClientRRProc::processGetRecScraperEventType()
2421 #if VDRVERSNUM >= 20301
2422 LOCK_RECORDINGS_READ;
2423 const cRecordings* tRecordings = Recordings;
2425 cThreadLock RecordingsLock(&Recordings);
2426 cRecordings* tRecordings = &Recordings;
2429 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2430 ScraperGetEventType call;
2433 if (recording && x.scrapQuery())
2435 call.recording = recording;
2436 x.scraper->Service("GetEventType", &call);
2438 resp->addUCHAR(call.type);
2439 if (call.type == tMovie)
2441 resp->addLONG(call.movieId);
2443 else if (call.type == tSeries)
2445 resp->addLONG(call.seriesId);
2446 resp->addLONG(call.episodeId);
2449 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2454 int VompClientRRProc::processGetEventScraperEventType()
2456 ScraperGetEventType call;
2458 ULONG channelid = ntohl(*(ULONG*)req->data);
2459 ULONG eventid = ntohl(*(ULONG*)(req->data+4));
2460 const cEvent *event = NULL;
2462 #if VDRVERSNUM >= 20301
2464 const cChannels* tChannels = Channels;
2466 cChannels* tChannels = &Channels;
2469 const cChannel* channel = tChannels->GetByNumber(channelid);
2471 #if VDRVERSNUM < 10300
2472 cMutexLock MutexLock;
2473 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
2474 #elif VDRVERSNUM < 20301
2475 cSchedulesLock MutexLock;
2476 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
2478 LOCK_SCHEDULES_READ;
2479 const cSchedules *tSchedules = Schedules;
2482 if (tSchedules && channel)
2484 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
2486 event = Schedule->GetEvent(eventid);
2490 if (event && x.scrapQuery())
2493 x.scraper->Service("GetEventType",&call);
2495 resp->addUCHAR(call.type);
2496 if (call.type == tMovie)
2498 resp->addLONG(call.movieId);
2499 } else if (call.type == tSeries){
2500 resp->addLONG(call.seriesId);
2501 resp->addLONG(call.episodeId);
2503 if (x.pict->epgImageExists(eventid)) {
2510 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2515 #define ADDSTRING_TO_PAKET(y) if ((y)!=0) resp->addString(x.charconvutf8->Convert(y)); else resp->addString("");
2517 int VompClientRRProc::processGetScraperMovieInfo()
2521 movie.movieId = ntohl(*(ULONG*)req->data);
2522 if (!x.scrapQuery()) {
2523 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2524 return 0; //stupid, I have no scraper why are you still asking
2526 x.scraper->Service("GetMovie",&movie);
2529 ADDSTRING_TO_PAKET(movie.title.c_str());
2530 ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
2531 ADDSTRING_TO_PAKET(movie.tagline.c_str());
2532 ADDSTRING_TO_PAKET(movie.overview.c_str());
2533 resp->addUCHAR(movie.adult);
2534 ADDSTRING_TO_PAKET(movie.collectionName.c_str());
2536 resp->addLONG(movie.budget);
2537 resp->addLONG(movie.revenue);
2538 ADDSTRING_TO_PAKET(movie.genres.c_str());
2539 ADDSTRING_TO_PAKET(movie.homepage.c_str());
2540 ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
2541 resp->addLONG(movie.runtime);
2542 resp->adddouble(movie.popularity);
2543 resp->adddouble(movie.voteAverage);
2544 resp->addULONG(movie.poster.width);
2545 resp->addULONG(movie.poster.height);
2546 resp->addULONG(movie.fanart.width);
2547 resp->addULONG(movie.fanart.height);
2548 resp->addULONG(movie.collectionPoster.width);
2549 resp->addULONG(movie.collectionPoster.height);
2550 resp->addULONG(movie.collectionFanart.width);
2551 resp->addULONG(movie.collectionFanart.height);
2552 resp->addULONG(movie.actors.size());
2553 for (ULONG acty=0; acty < movie.actors.size(); acty++) {
2554 ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
2555 ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
2556 resp->addULONG(movie.actors[acty].actorThumb.width);
2557 resp->addULONG(movie.actors[acty].actorThumb.height);
2561 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2568 int VompClientRRProc::processGetScraperSeriesInfo()
2571 series.seriesId = ntohl(*(ULONG*)req->data);
2572 series.episodeId = ntohl(*(ULONG*)(req->data+4));
2573 if (!x.scrapQuery()) {
2574 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2575 return 0; //stupid, I have no scraper why are you still asking
2577 x.scraper->Service("GetSeries",&series);
2579 ADDSTRING_TO_PAKET(series.name.c_str());
2580 ADDSTRING_TO_PAKET(series.overview.c_str());
2581 ADDSTRING_TO_PAKET(series.firstAired.c_str());
2582 ADDSTRING_TO_PAKET(series.network.c_str());
2583 ADDSTRING_TO_PAKET(series.genre.c_str());
2584 resp->adddouble(series.rating);
2585 ADDSTRING_TO_PAKET(series.status.c_str());
2587 resp->addLONG(series.episode.number);
2588 resp->addLONG(series.episode.season);
2589 ADDSTRING_TO_PAKET(series.episode.name.c_str());
2590 ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
2591 ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
2592 ADDSTRING_TO_PAKET(series.episode.overview.c_str());
2593 resp->adddouble(series.episode.rating);
2594 resp->addULONG(series.episode.episodeImage.width);
2595 resp->addULONG(series.episode.episodeImage.height);
2597 ULONG num_actors = series.actors.size();
2598 resp->addULONG(num_actors);
2599 for (ULONG acty=0; acty < num_actors; acty++) {
2600 ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
2601 ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
2602 resp->addULONG(series.actors[acty].actorThumb.width);
2603 resp->addULONG(series.actors[acty].actorThumb.height);
2605 ULONG num_posters = series.posters.size();
2606 resp->addULONG(num_posters);
2607 for (ULONG medias = 0; medias < num_posters; medias++ ) {
2608 cTvMedia media=series.posters[medias];
2609 resp->addULONG(media.width);
2610 resp->addULONG(media.height);
2613 ULONG num_banners = series.banners.size();
2614 resp->addULONG(num_banners);
2615 for (ULONG medias = 0; medias < num_banners; medias++ ) {
2616 cTvMedia media=series.banners[medias];
2617 resp->addULONG(media.width);
2618 resp->addULONG(media.height);
2620 ULONG num_fanarts = series.fanarts.size();
2621 resp->addULONG(num_fanarts);
2622 for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
2623 cTvMedia media=series.fanarts[medias];
2624 resp->addULONG(media.width);
2625 resp->addULONG(media.height);
2627 resp->addULONG(series.seasonPoster.width);
2628 resp->addULONG(series.seasonPoster.height);
2632 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2637 int VompClientRRProc::processLoadTvMedia()
2639 TVMediaRequest tvreq;
2640 tvreq.streamID = req->requestID;
2641 tvreq.type = ntohl(*(ULONG*)req->data);
2642 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2643 tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
2644 tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
2645 tvreq.container = ntohl(*(ULONG*)(req->data+16));
2646 tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
2647 log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
2648 x.pict->addTVMediaRequest(tvreq);
2653 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2658 int VompClientRRProc::processLoadTvMediaRecThumb()
2660 TVMediaRequest tvreq;
2661 tvreq.streamID = req->requestID;
2662 tvreq.type = 3; // unknown but primary_name is set
2663 tvreq.primary_id = 0;
2664 tvreq.primary_name = std::string((const char*) req->data);
2665 tvreq.secondary_id = 0;
2666 tvreq.type_pict = 1;
2667 tvreq.container = 0;
2668 tvreq.container_member = 0;
2669 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2670 x.pict->addTVMediaRequest(tvreq);
2675 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2680 int VompClientRRProc::processLoadTvMediaEventThumb()
2682 TVMediaRequest tvreq;
2683 tvreq.streamID = req->requestID;
2684 tvreq.type = 4; // unknown but primary_id is set
2685 UINT channelid = ntohl(*(ULONG*)req->data);
2686 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2687 tvreq.secondary_id = 0;
2689 #if VDRVERSNUM >= 20301
2691 const cChannels* tChannels = Channels;
2693 cChannels* tChannels = &Channels;
2696 const cChannel* channel = tChannels->GetByNumber(channelid);
2698 if (channel) tvreq.primary_name = std::string((const char*)channel->GetChannelID().ToString());
2699 tvreq.type_pict = 1;
2700 tvreq.container = 0;
2701 tvreq.container_member = 0;
2702 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2703 x.pict->addTVMediaRequest(tvreq);
2708 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2713 int VompClientRRProc::processLoadChannelLogo()
2715 TVMediaRequest tvreq;
2716 tvreq.streamID = req->requestID;
2717 tvreq.type = 5; // channel logo
2718 UINT channelid = ntohl(*(ULONG*)req->data);
2719 tvreq.primary_id = channelid;
2720 tvreq.secondary_id = 0;
2722 #if VDRVERSNUM >= 20301
2724 const cChannels* tChannels = Channels;
2726 cChannels* tChannels = &Channels;
2729 const cChannel* channel = tChannels->GetByNumber(channelid);
2731 if (channel) tvreq.primary_name = std::string((const char*)channel->Name());
2732 tvreq.type_pict = 1;
2733 tvreq.container = 0;
2734 tvreq.container_member = 0;
2735 if (channel) log->log("RRProc", Log::DEBUG, "TVMedia request %d %d %s",req->requestID,channelid, channel->Name());
2736 x.pict->addTVMediaRequest(tvreq);
2741 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2749 #endif // !VOMPSTANDALONE