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 "recplayer.h"
31 #include "mvpreceiver.h"
34 #include "vompclientrrproc.h"
35 #include "vompclient.h"
40 VompClientRRProc::VompClientRRProc(VompClient& x)
43 log = Log::getInstance();
48 bool VompClientRRProc::init()
53 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
57 Currently only one at once is supported but this
58 could be upgraded to a queueing system
65 log->log("RRProc", Log::ERR, "recvReq err 1");
73 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
79 void VompClientRRProc::threadMethod()
82 log->log("RRProc", Log::DEBUG, "threadMethod startup");
88 log->log("RRProc", Log::ERR, "threadMethod err 1");
93 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
94 threadWaitForSignal();
97 log->log("RRProc", Log::ERR, "threadMethod err 2");
102 log->log("RRProc", Log::DEBUG, "thread woken with req");
108 bool VompClientRRProc::processPacket()
110 resp = new ResponsePacket();
111 if (!resp->init(req->requestID))
113 log->log("RRProc", Log::ERR, "response packet init fail");
123 result = processLogin();
125 #ifndef VOMPSTANDALONE
127 result = processGetRecordingsList();
130 result = processDeleteRecording();
133 result = processGetChannelsList();
136 result = processStartStreamingChannel();
139 result = processGetBlock();
142 result = processStopStreaming();
145 result = processStartStreamingRecording();
149 result = processGetChannelSchedule();
152 result = processConfigSave();
155 result = processConfigLoad();
157 #ifndef VOMPSTANDALONE
159 result = processReScanRecording(); // FIXME obselete
162 result = processGetTimers();
165 result = processSetTimer();
168 result = processPositionFromFrameNumber();
171 result = processFrameNumberFromPosition();
174 result = processMoveRecording();
177 result = processGetIFrame();
180 result = processGetRecInfo();
183 result = processGetMarks();
186 result = processGetChannelPids();
189 result = processDeleteTimer();
193 result = processGetMediaList();
196 result = processGetPicture();
199 result = processGetImageBlock();
202 result = processGetLanguageList();
205 result = processGetLanguageContent();
212 if (req->data) free(req->data);
216 if (result) return true;
220 int VompClientRRProc::processLogin()
222 if (req->dataLength != 6) return 0;
226 char configFileName[PATH_MAX];
227 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]);
228 x.config.init(configFileName);
230 // Send the login reply
232 time_t timeNow = time(NULL);
233 struct tm* timeStruct = localtime(&timeNow);
234 int timeOffset = timeStruct->tm_gmtoff;
236 resp->addULONG(timeNow);
237 resp->addLONG(timeOffset);
239 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
240 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
246 int VompClientRRProc::processConfigSave()
248 char* section = (char*)req->data;
252 for (UINT k = 0; k < req->dataLength; k++)
254 if (req->data[k] == '\0')
258 key = (char*)&req->data[k+1];
262 value = (char*)&req->data[k+1];
268 // if the last string (value) doesnt have null terminator, give up
269 if (req->data[req->dataLength - 1] != '\0') return 0;
271 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
272 if (x.config.setValueString(section, key, value))
282 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
287 int VompClientRRProc::processConfigLoad()
289 char* section = (char*)req->data;
292 for (UINT k = 0; k < req->dataLength; k++)
294 if (req->data[k] == '\0')
296 key = (char*)&req->data[k+1];
301 char* value = x.config.getValueString(section, key);
305 resp->addString(value);
306 log->log("RRProc", Log::DEBUG, "Written config load packet");
312 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
316 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
322 * media List Request:
325 * 4 flags (currently unused)
328 * Media List response:
336 * 4 strlen (incl. 0 Byte)
341 int VompClientRRProc::processGetMediaList()
343 if (req->dataLength < 4) {
344 log->log("RRProc", Log::ERR, "getMediaList packet too short %d", req->dataLength);
348 if (req->dataLength > 4) {
349 //we have a dirname provided
350 dirname=(char *)&req->data[4];
351 log->log("RRProc", Log::DEBUG, "getMediaList for %s", dirname);
354 MediaList * ml=MediaList::readList(x.baseConfig, dirname);
356 log->log("RRProc", Log::ERR, "getMediaList returned NULL");
360 //response code (not yet set)
364 resp->addULONG(ml->size());
366 for (MediaList::iterator nm=ml->begin(); nm<ml->end(); nm++)
369 log->log("RRProc", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
370 resp->addULONG(m->getType());
372 resp->addULONG(m->getTime());
375 int len=strlen(m->getFilename());
377 resp->addULONG(len+1);
378 resp->addString(m->getFilename());
383 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
385 log->log("RRProc", Log::DEBUG, "Written Media list");
391 * 4 flags (currently unused)
396 * get image response:
402 int VompClientRRProc::processGetPicture()
404 if (req->dataLength < 12) {
405 log->log("RRProc", Log::ERR, "getPicture packet too short %d", req->dataLength);
412 char * filename=NULL;
413 if (req->dataLength > 12) {
414 //we have a dirname provided
415 filename=(char *)&req->data[12];
416 log->log("RRProc", Log::DEBUG, "getPicture %s", filename);
419 log->log("RRProc", Log::ERR, "getPicture empty filename");
422 x.imageFile=fopen(filename,"r");
423 if (!x.imageFile) log->log("RRProc", Log::ERR, "getPicture unable to open %s",filename);
428 if ( fstat(fileno(x.imageFile),&st) == 0) size=st.st_size;
430 //response code (not yet set)
433 resp->addULONG(size);
435 log->log("RRProc", Log::DEBUG, "getPicture size %u", size);
438 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
440 log->log("RRProc", Log::DEBUG, "Written getPicture");
446 int VompClientRRProc::processGetImageBlock()
450 log->log("RRProc", Log::DEBUG, "Get image block called when no image active");
454 UCHAR* data = req->data;
456 ULLONG position = x.ntohll(*(ULLONG*)data);
457 data += sizeof(ULLONG);
458 ULONG amount = ntohl(*(ULONG*)data);
460 log->log("RRProc", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
462 UCHAR sendBuffer[amount];
463 ULONG amountReceived = 0; // compiler moan.
464 ULLONG cpos=ftell(x.imageFile);
465 if (position != cpos) {
466 fseek(x.imageFile,position-cpos,SEEK_CUR);
468 if (position != (ULLONG)ftell(x.imageFile)) {
469 log->log("RRProc", Log::DEBUG, "getImageblock pos = %llu not available", position);
472 amountReceived=fread(&sendBuffer[0],1,amount,x.imageFile);
478 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
482 resp->copyin(sendBuffer, amount);
483 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
487 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
493 int VompClientRRProc::processGetLanguageList()
495 x.i18n.findLanguages();
496 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
498 I18n::lang_code_list::const_iterator iter;
499 for (iter = languages.begin(); iter != languages.end(); ++iter)
501 resp->addString(iter->first.c_str());
502 resp->addString(iter->second.c_str());
505 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
509 int VompClientRRProc::processGetLanguageContent()
511 if (req->dataLength <= 0) return 0;
512 std::string code, result;
513 code.assign((char*)req->data, req->dataLength - 1);
514 x.i18n.findLanguages();
515 I18n::trans_table texts = x.i18n.getLanguageContent(code);
516 I18n::trans_table::const_iterator iter;
517 for (iter = texts.begin(); iter != texts.end(); ++iter)
519 resp->addString(iter->first.c_str());
520 resp->addString(iter->second.c_str());
523 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
527 #ifndef VOMPSTANDALONE
529 int VompClientRRProc::processGetRecordingsList()
532 int Percent = VideoDiskSpace(&FreeMB);
533 int Total = (FreeMB / (100 - Percent)) * 100;
535 resp->addULONG(Total);
536 resp->addULONG(FreeMB);
537 resp->addULONG(Percent);
539 cRecordings Recordings;
542 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
544 resp->addULONG(recording->start);
545 resp->addString(recording->Name());
546 resp->addString(recording->FileName());
550 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
552 log->log("RRProc", Log::DEBUG, "Written recordings list");
557 int VompClientRRProc::processDeleteRecording()
559 // data is a pointer to the fileName string
561 cRecordings Recordings;
562 Recordings.Load(); // probably have to do this
564 cRecording* recording = Recordings.GetByName((char*)req->data);
566 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
570 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
572 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
575 if (recording->Delete())
577 // Copy svdrp's way of doing this, see if it works
578 #if VDRVERSNUM > 10300
579 ::Recordings.DelByName(recording->FileName());
599 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
604 int VompClientRRProc::processMoveRecording()
606 log->log("RRProc", Log::DEBUG, "Process move recording");
607 char* fileName = (char*)req->data;
608 char* newPath = NULL;
610 for (UINT k = 0; k < req->dataLength; k++)
612 if (req->data[k] == '\0')
614 newPath = (char*)&req->data[k+1];
618 if (!newPath) return 0;
620 cRecordings Recordings;
621 Recordings.Load(); // probably have to do this
623 cRecording* recording = Recordings.GetByName((char*)fileName);
625 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
629 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
632 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
633 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
634 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
636 const char* t = recording->FileName();
638 char* dateDirName = NULL; int k;
639 char* titleDirName = NULL; int j;
641 // Find the datedirname
642 for(k = strlen(t) - 1; k >= 0; k--)
646 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
647 dateDirName = new char[strlen(&t[k+1]) + 1];
648 strcpy(dateDirName, &t[k+1]);
653 // Find the titledirname
655 for(j = k-1; j >= 0; j--)
659 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
660 titleDirName = new char[(k - j - 1) + 1];
661 memcpy(titleDirName, &t[j+1], k - j - 1);
662 titleDirName[k - j - 1] = '\0';
667 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
668 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
669 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
671 char* newPathConv = new char[strlen(newPath)+1];
672 strcpy(newPathConv, newPath);
673 ExchangeChars(newPathConv, true);
674 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
676 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
677 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
678 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
679 delete[] newPathConv;
681 log->log("RRProc", Log::DEBUG, "%s", newContainer);
684 int statret = stat(newContainer, &dstat);
685 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
687 log->log("RRProc", Log::DEBUG, "new dir does not exist");
688 int mkdirret = mkdir(newContainer, 0755);
691 delete[] dateDirName;
692 delete[] titleDirName;
693 delete[] newContainer;
697 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
701 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
703 delete[] dateDirName;
704 delete[] titleDirName;
705 delete[] newContainer;
709 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
713 // Ok, the directory container has been made, or it pre-existed.
715 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
716 sprintf(newDir, "%s/%s", newContainer, dateDirName);
718 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
719 int renameret = rename(t, newDir);
722 // Success. Test for remove old dir containter
723 char* oldTitleDir = new char[k+1];
724 memcpy(oldTitleDir, t, k);
725 oldTitleDir[k] = '\0';
726 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
727 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
728 delete[] oldTitleDir;
733 #if VDRVERSNUM > 10311
735 ::Recordings.Update();
737 // Success. Send a different packet from just a ulong
738 resp->addULONG(1); // success
739 resp->addString(newDir);
747 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
749 delete[] dateDirName;
750 delete[] titleDirName;
751 delete[] newContainer;
758 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
765 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
771 int VompClientRRProc::processGetChannelsList()
775 char* chanConfig = x.config.getValueString("General", "Channels");
777 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
779 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
781 #if VDRVERSNUM < 10300
782 if (!channel->GroupSep() && (!channel->Ca() || allChans))
784 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
787 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
789 if (channel->Vpid()) type = 1;
790 #if VDRVERSNUM < 10300
793 else if (channel->Apid(0)) type = 2;
797 resp->addULONG(channel->Number());
798 resp->addULONG(type);
799 resp->addString(channel->Name());
804 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
806 log->log("RRProc", Log::DEBUG, "Written channels list");
811 int VompClientRRProc::processGetChannelPids()
813 ULONG channelNumber = ntohl(*(ULONG*)req->data);
815 cChannel* channel = x.channelFromNumber(channelNumber);
820 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
829 #if VDRVERSNUM < 10300
831 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
832 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
834 if (channel->Apid2())
836 else if (channel->Apid1())
843 for (const int *Apid = channel->Apids(); *Apid; Apid++)
847 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
851 for (const int *Spid = channel->Spids(); *Spid; Spid++)
858 // Format of response
877 resp->addULONG(channel->Vpid());
878 resp->addULONG(numApids);
880 #if VDRVERSNUM < 10300
883 resp->addULONG(channel->Apid1());
888 resp->addULONG(channel->Apid2());
894 for (ULONG i = 0; i < numApids; i++)
896 resp->addULONG(channel->Apid(i));
897 resp->addString(channel->Alang(i));
899 resp->addULONG(numDpids);
900 for (ULONG i = 0; i < numDpids; i++)
902 resp->addULONG(channel->Dpid(i));
903 resp->addString(channel->Dlang(i));
905 resp->addULONG(numSpids);
906 for (ULONG i = 0; i < numSpids; i++)
908 resp->addULONG(channel->Spid(i));
909 resp->addString(channel->Slang(i));
912 resp->addULONG(channel->Tpid());
916 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
918 log->log("RRProc", Log::DEBUG, "Written channels pids");
923 int VompClientRRProc::processStartStreamingChannel()
927 log->log("RRProc", Log::ERR, "Client called start streaming twice");
931 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
932 ULONG channelNumber = ntohl(*(ULONG*)req->data);
934 cChannel* channel = x.channelFromNumber(channelNumber);
939 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
943 // get the priority we should use
945 int priority = x.config.getValueLong("General", "Live priority", &fail);
948 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
952 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
957 if (priority < 0) priority = 0;
958 if (priority > 99) priority = 99;
960 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
961 x.lp = MVPReceiver::create(channel, priority);
967 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
971 if (!x.lp->init(&x.tcp, req->requestID))
977 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
983 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
987 int VompClientRRProc::processStopStreaming()
989 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
995 else if (x.recplayer)
1000 delete x.recordingManager;
1002 x.recordingManager = NULL;
1007 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1011 int VompClientRRProc::processGetBlock()
1013 if (!x.lp && !x.recplayer)
1015 log->log("RRProc", Log::DEBUG, "Get block called when no streaming happening!");
1019 UCHAR* data = req->data;
1021 ULLONG position = x.ntohll(*(ULLONG*)data);
1022 data += sizeof(ULLONG);
1023 ULONG amount = ntohl(*(ULONG*)data);
1025 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1027 UCHAR sendBuffer[amount];
1028 ULONG amountReceived = 0; // compiler moan.
1031 log->log("RRProc", Log::DEBUG, "getting from live");
1032 amountReceived = x.lp->getBlock(&sendBuffer[0], amount);
1034 if (!amountReceived)
1036 // vdr has possibly disconnected the receiver
1037 log->log("RRProc", Log::DEBUG, "VDR has disconnected the live receiver");
1042 else if (x.recplayer)
1044 log->log("RRProc", Log::DEBUG, "getting from recording");
1045 amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1048 if (!amountReceived)
1051 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1055 resp->copyin(sendBuffer, amountReceived);
1056 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1060 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1061 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1065 int VompClientRRProc::processStartStreamingRecording()
1067 // data is a pointer to the fileName string
1069 x.recordingManager = new cRecordings;
1070 x.recordingManager->Load();
1072 cRecording* recording = x.recordingManager->GetByName((char*)req->data);
1074 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1078 x.recplayer = new RecPlayer(recording);
1080 resp->addULLONG(x.recplayer->getLengthBytes());
1081 resp->addULONG(x.recplayer->getLengthFrames());
1083 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1085 log->log("RRProc", Log::DEBUG, "written totalLength");
1089 delete x.recordingManager;
1090 x.recordingManager = NULL;
1095 int VompClientRRProc::processPositionFromFrameNumber()
1099 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1103 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1107 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1110 resp->addULLONG(retval);
1112 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1114 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1118 int VompClientRRProc::processFrameNumberFromPosition()
1122 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1126 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1130 retval = x.recplayer->frameNumberFromPosition(position);
1133 resp->addULONG(retval);
1135 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1137 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1141 int VompClientRRProc::processGetIFrame()
1143 bool success = false;
1145 ULONG* data = (ULONG*)req->data;
1147 ULONG frameNumber = ntohl(*data);
1149 ULONG direction = ntohl(*data);
1151 ULLONG rfilePosition = 0;
1152 ULONG rframeNumber = 0;
1153 ULONG rframeLength = 0;
1157 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1161 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1164 // returns file position, frame number, length
1168 resp->addULLONG(rfilePosition);
1169 resp->addULONG(rframeNumber);
1170 resp->addULONG(rframeLength);
1178 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1180 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1184 int VompClientRRProc::processGetChannelSchedule()
1186 ULONG* data = (ULONG*)req->data;
1188 ULONG channelNumber = ntohl(*data);
1190 ULONG startTime = ntohl(*data);
1192 ULONG duration = ntohl(*data);
1194 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1196 cChannel* channel = x.channelFromNumber(channelNumber);
1201 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1203 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1207 log->log("RRProc", Log::DEBUG, "Got channel");
1209 #if VDRVERSNUM < 10300
1210 cMutexLock MutexLock;
1211 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1213 cSchedulesLock MutexLock;
1214 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1220 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1222 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1226 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1228 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1233 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1235 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1239 log->log("RRProc", Log::DEBUG, "Got schedule object");
1241 const char* empty = "";
1242 bool atLeastOneEvent = false;
1245 ULONG thisEventTime;
1246 ULONG thisEventDuration;
1247 const char* thisEventTitle;
1248 const char* thisEventSubTitle;
1249 const char* thisEventDescription;
1251 #if VDRVERSNUM < 10300
1253 const cEventInfo *event;
1254 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1256 event = Schedule->GetEventNumber(eventNumber);
1258 thisEventID = event->GetEventID();
1259 thisEventTime = event->GetTime();
1260 thisEventDuration = event->GetDuration();
1261 thisEventTitle = event->GetTitle();
1262 thisEventSubTitle = event->GetSubtitle();
1263 thisEventDescription = event->GetExtendedDescription();
1267 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1269 thisEventID = event->EventID();
1270 thisEventTime = event->StartTime();
1271 thisEventDuration = event->Duration();
1272 thisEventTitle = event->Title();
1273 thisEventSubTitle = NULL;
1274 thisEventDescription = event->Description();
1278 //in the past filter
1279 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1282 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1285 if (thisEventTime >= (startTime + duration)) continue;
1287 if (!thisEventTitle) thisEventTitle = empty;
1288 if (!thisEventSubTitle) thisEventSubTitle = empty;
1289 if (!thisEventDescription) thisEventDescription = empty;
1291 resp->addULONG(thisEventID);
1292 resp->addULONG(thisEventTime);
1293 resp->addULONG(thisEventDuration);
1295 resp->addString(thisEventTitle);
1296 resp->addString(thisEventSubTitle);
1297 resp->addString(thisEventDescription);
1299 atLeastOneEvent = true;
1302 log->log("RRProc", Log::DEBUG, "Got all event data");
1304 if (!atLeastOneEvent)
1307 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1311 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1313 log->log("RRProc", Log::DEBUG, "written schedules packet");
1318 int VompClientRRProc::processGetTimers()
1321 int numTimers = Timers.Count();
1323 resp->addULONG(numTimers);
1325 for (int i = 0; i < numTimers; i++)
1327 timer = Timers.Get(i);
1329 #if VDRVERSNUM < 10300
1330 resp->addULONG(timer->Active());
1332 resp->addULONG(timer->HasFlags(tfActive));
1334 resp->addULONG(timer->Recording());
1335 resp->addULONG(timer->Pending());
1336 resp->addULONG(timer->Priority());
1337 resp->addULONG(timer->Lifetime());
1338 resp->addULONG(timer->Channel()->Number());
1339 resp->addULONG(timer->StartTime());
1340 resp->addULONG(timer->StopTime());
1341 resp->addULONG(timer->Day());
1342 resp->addULONG(timer->WeekDays());
1343 resp->addString(timer->File());
1347 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1349 log->log("RRProc", Log::DEBUG, "Written timers list");
1354 int VompClientRRProc::processSetTimer()
1356 char* timerString = new char[strlen((char*)req->data) + 1];
1357 strcpy(timerString, (char*)req->data);
1359 #if VDRVERSNUM < 10300
1361 // If this is VDR 1.2 the date part of the timer string must be reduced
1362 // to just DD rather than YYYY-MM-DD
1364 int s = 0; // source
1365 int d = 0; // destination
1367 while(c != 2) // copy up to date section, including the second ':'
1369 timerString[d] = req->data[s];
1370 if (req->data[s] == ':') c++;
1374 // now it has copied up to the date section
1376 while(c != 2) // waste YYYY-MM-
1378 if (req->data[s] == '-') c++;
1381 // now source is at the DD
1382 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1383 d += req->dataLength - s;
1384 timerString[d] = '\0';
1386 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1387 log->log("RRProc", Log::DEBUG, "%s", timerString);
1391 cTimer *timer = new cTimer;
1392 if (timer->Parse((char*)timerString))
1394 cTimer *t = Timers.GetTimer(timer);
1398 #if VDRVERSNUM < 10300
1401 Timers.SetModified();
1405 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1412 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1419 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1425 int VompClientRRProc::processDeleteTimer()
1427 log->log("RRProc", Log::DEBUG, "Delete timer called");
1432 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1433 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1434 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1435 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1436 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1439 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1441 if ( (ti->Channel()->Number() == delChannel)
1442 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1443 && (ti->StartTime() == delStart)
1444 && (ti->StopTime() == delStop) )
1452 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1456 if (!Timers.BeingEdited())
1458 if (!ti->Recording())
1461 Timers.SetModified();
1464 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1469 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1472 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1478 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1481 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1486 int VompClientRRProc::processGetRecInfo()
1488 // data is a pointer to the fileName string
1490 cRecordings Recordings;
1491 Recordings.Load(); // probably have to do this
1493 cRecording *recording = Recordings.GetByName((char*)req->data);
1495 time_t timerStart = 0;
1496 time_t timerStop = 0;
1497 char* summary = NULL;
1498 ULONG resumePoint = 0;
1502 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1505 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1510 4 bytes: start time for timer
1511 4 bytes: end time for timer
1512 4 bytes: resume point
1514 4 bytes: num components
1524 // Get current timer
1526 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1529 timerStart = rc->Timer()->StartTime();
1530 timerStop = rc->Timer()->StopTime();
1531 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1534 resp->addULONG(timerStart);
1535 resp->addULONG(timerStop);
1539 char* value = x.config.getValueString("ResumeData", (char*)req->data);
1542 resumePoint = strtoul(value, NULL, 10);
1545 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1547 resp->addULONG(resumePoint);
1552 #if VDRVERSNUM < 10300
1553 summary = (char*)recording->Summary();
1555 const cRecordingInfo *Info = recording->Info();
1556 summary = (char*)Info->ShortText();
1557 if (isempty(summary)) summary = (char*)Info->Description();
1559 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
1562 resp->addString(summary);
1566 resp->addString("");
1571 #if VDRVERSNUM < 10300
1573 // Send 0 for numchannels - this signals the client this info is not available
1577 const cComponents* components = Info->Components();
1579 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
1587 resp->addULONG(components->NumComponents());
1589 tComponent* component;
1590 for (int i = 0; i < components->NumComponents(); i++)
1592 component = components->Component(i);
1594 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1596 resp->addUCHAR(component->stream);
1597 resp->addUCHAR(component->type);
1599 if (component->language)
1601 resp->addString(component->language);
1605 resp->addString("");
1607 if (component->description)
1609 resp->addString(component->description);
1613 resp->addString("");
1623 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1625 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
1635 int VompClientRRProc::processReScanRecording()
1639 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1643 x.recplayer->scan();
1645 resp->addULLONG(x.recplayer->getLengthBytes());
1646 resp->addULONG(x.recplayer->getLengthFrames());
1648 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1649 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
1653 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1655 int VompClientRRProc::processGetMarks()
1657 // data is a pointer to the fileName string
1660 cRecordings Recordings;
1661 Recordings.Load(); // probably have to do this
1663 cRecording *recording = Recordings.GetByName((char*)req->data);
1665 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1669 Marks.Load(recording->FileName());
1672 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1674 log->log("RRProc", Log::DEBUG, "found Mark %i", m->position);
1676 resp->addULONG(m->position);
1681 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
1687 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1689 log->log("RRProc", Log::DEBUG, "Written Marks list");
1694 #endif // !VOMPSTANDALONE