2 Copyright 2004-2005 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.
21 #include "mvpclient.h"
23 #include "responsepacket.h"
25 // This is here else it causes compile errors with something in libdvbmpeg
26 //#include <vdr/menu.h>
28 pthread_mutex_t threadClientMutex;
29 int MVPClient::nr_clients = 0;
32 MVPClient::MVPClient(Config* cfgBase, char* tconfigDir, int tsocket)
33 : tcp(tsocket), i18n(tconfigDir)
35 #ifndef VOMPSTANDALONE
38 recordingManager = NULL;
41 log = Log::getInstance();
43 configDir = tconfigDir;
44 log->log("Client", Log::DEBUG, "Config dir: %s", configDir);
49 MVPClient::~MVPClient()
51 log->log("Client", Log::DEBUG, "MVP client destructor");
52 #ifndef VOMPSTANDALONE
63 delete recordingManager;
65 recordingManager = NULL;
68 if (loggedIn) cleanConfig();
72 void MVPClient::incClients()
74 pthread_mutex_lock(&threadClientMutex);
75 MVPClient::nr_clients++;
76 pthread_mutex_unlock(&threadClientMutex);
79 void MVPClient::decClients()
81 pthread_mutex_lock(&threadClientMutex);
82 MVPClient::nr_clients--;
83 pthread_mutex_unlock(&threadClientMutex);
86 int MVPClient::getNrClients()
89 pthread_mutex_lock(&threadClientMutex);
90 nrClients = MVPClient::nr_clients;
91 pthread_mutex_unlock(&threadClientMutex);
95 ULLONG MVPClient::ntohll(ULLONG a)
100 ULLONG MVPClient::htonll(ULLONG a)
102 #if BYTE_ORDER == BIG_ENDIAN
107 b = ((a << 56) & 0xFF00000000000000ULL)
108 | ((a << 40) & 0x00FF000000000000ULL)
109 | ((a << 24) & 0x0000FF0000000000ULL)
110 | ((a << 8) & 0x000000FF00000000ULL)
111 | ((a >> 8) & 0x00000000FF000000ULL)
112 | ((a >> 24) & 0x0000000000FF0000ULL)
113 | ((a >> 40) & 0x000000000000FF00ULL)
114 | ((a >> 56) & 0x00000000000000FFULL) ;
120 #ifndef VOMPSTANDALONE
121 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
123 cChannel* channel = NULL;
125 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
127 if (!channel->GroupSep())
129 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
131 if (channel->Number() == (int)channelNumber)
133 int vpid = channel->Vpid();
134 #if VDRVERSNUM < 10300
135 int apid1 = channel->Apid1();
137 int apid1 = channel->Apid(0);
139 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
147 log->log("Client", Log::DEBUG, "Channel not found");
153 void MVPClient::writeResumeData()
155 config.setValueLong("ResumeData",
156 (char*)recplayer->getCurrentRecording()->FileName(),
157 recplayer->frameNumberFromPosition(recplayer->getLastPosition()) );
161 void MVPClient::cleanConfig()
163 log->log("Client", Log::DEBUG, "Clean config");
165 #ifndef VOMPSTANDALONE
167 cRecordings Recordings;
172 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
173 char* position = resumes;
174 for(int k = 0; k < numReturns; k++)
176 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
178 cRecording* recording = Recordings.GetByName(position);
181 // doesn't exist anymore
182 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
183 config.deleteValue("ResumeData", position);
187 log->log("Client", Log::DEBUG, "This recording still exists");
190 position += strlen(position) + 1;
197 void MVPClientStartThread(void* arg)
199 MVPClient* m = (MVPClient*)arg;
201 // Nothing external to this class has a reference to it
202 // This is the end of the thread.. so delete m
209 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
210 log->log("Client", Log::DEBUG, "MVPClient run success");
214 void MVPClient::run2()
219 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
220 pthread_detach(runThread); // Detach
222 tcp.disableReadTimeout();
224 tcp.setSoKeepTime(3);
225 tcp.setNonBlocking();
230 ULONG extraDataLength;
236 log->log("Client", Log::DEBUG, "Waiting");
239 if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG))) break;
240 channelID = ntohl(channelID);
243 log->log("Client", Log::ERR, "Incoming channel number not 1!");
247 log->log("Client", Log::DEBUG, "Got chan");
249 if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
250 requestID = ntohl(requestID);
252 log->log("Client", Log::DEBUG, "Got ser");
254 if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
255 opcode = ntohl(opcode);
257 log->log("Client", Log::DEBUG, "Got op %lu", opcode);
259 if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
260 extraDataLength = ntohl(extraDataLength);
261 if (extraDataLength > 200000)
263 log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
267 log->log("Client", Log::DEBUG, "Got edl %lu", extraDataLength);
271 data = (UCHAR*)malloc(extraDataLength);
274 log->log("Client", Log::ERR, "Extra data buffer malloc error");
278 if (!tcp.readData(data, extraDataLength))
280 log->log("Client", Log::ERR, "Could not read extradata");
290 log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
292 if (!loggedIn && (opcode != 1))
294 log->log("Client", Log::ERR, "Not logged in and opcode != 1");
295 if (data) free(data);
299 ResponsePacket* rp = new ResponsePacket();
300 if (!rp->init(requestID))
302 log->log("Client", Log::ERR, "response packet init fail");
310 result = processLogin(data, extraDataLength, rp);
312 #ifndef VOMPSTANDALONE
314 result = processGetRecordingsList(data, extraDataLength, rp);
317 result = processDeleteRecording(data, extraDataLength, rp);
320 result = processGetChannelsList(data, extraDataLength, rp);
323 result = processStartStreamingChannel(data, extraDataLength, requestID, rp);
326 result = processGetBlock(data, extraDataLength, rp);
329 result = processStopStreaming(data, extraDataLength, rp);
332 result = processStartStreamingRecording(data, extraDataLength, rp);
336 result = processGetChannelSchedule(data, extraDataLength, rp);
339 result = processConfigSave(data, extraDataLength, rp);
342 result = processConfigLoad(data, extraDataLength, rp);
344 #ifndef VOMPSTANDALONE
346 result = processReScanRecording(data, extraDataLength, rp); // FIXME obselete
349 result = processGetTimers(data, extraDataLength, rp);
352 result = processSetTimer(data, extraDataLength, rp);
355 result = processPositionFromFrameNumber(data, extraDataLength, rp);
358 result = processFrameNumberFromPosition(data, extraDataLength, rp);
361 result = processMoveRecording(data, extraDataLength, rp);
364 result = processGetIFrame(data, extraDataLength, rp);
367 result = processGetRecInfo(data, extraDataLength, rp);
370 result = processGetMarks(data, extraDataLength, rp);
373 result = processGetChannelPids(data, extraDataLength, rp);
376 result = processDeleteTimer(data, extraDataLength, rp);
380 result = processGetMediaList(data, extraDataLength, rp);
383 result = processGetPicture(data, extraDataLength, rp);
386 result = processGetImageBlock(data, extraDataLength, rp);
389 result = processGetLanguageList(data, extraDataLength, rp);
392 result = processGetLanguageContent(data, extraDataLength, rp);
397 if (data) free(data);
402 int MVPClient::processLogin(UCHAR* buffer, int length, ResponsePacket* rp)
404 if (length != 6) return 0;
408 char configFileName[PATH_MAX];
409 snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", configDir, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
410 config.init(configFileName);
412 // Send the login reply
414 time_t timeNow = time(NULL);
415 struct tm* timeStruct = localtime(&timeNow);
416 int timeOffset = timeStruct->tm_gmtoff;
418 rp->addULONG(timeNow);
419 rp->addLONG(timeOffset);
421 tcp.sendPacket(rp->getPtr(), rp->getLen());
422 log->log("Client", Log::DEBUG, "written login reply len %lu", rp->getLen());
428 #ifndef VOMPSTANDALONE
429 int MVPClient::processGetRecordingsList(UCHAR* data, int length, ResponsePacket* rp)
432 int Percent = VideoDiskSpace(&FreeMB);
433 int Total = (FreeMB / (100 - Percent)) * 100;
436 rp->addULONG(FreeMB);
437 rp->addULONG(Percent);
439 cRecordings Recordings;
442 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
444 rp->addULONG(recording->start);
445 rp->addString(recording->Name());
446 rp->addString(recording->FileName());
450 tcp.sendPacket(rp->getPtr(), rp->getLen());
452 log->log("Client", Log::DEBUG, "Written recordings list");
457 int MVPClient::processDeleteRecording(UCHAR* data, int length, ResponsePacket* rp)
459 // data is a pointer to the fileName string
461 cRecordings Recordings;
462 Recordings.Load(); // probably have to do this
464 cRecording* recording = Recordings.GetByName((char*)data);
466 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
470 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
472 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
475 if (recording->Delete())
477 // Copy svdrp's way of doing this, see if it works
478 #if VDRVERSNUM > 10300
479 ::Recordings.DelByName(recording->FileName());
499 tcp.sendPacket(rp->getPtr(), rp->getLen());
504 int MVPClient::processMoveRecording(UCHAR* data, int length, ResponsePacket* rp)
506 log->log("Client", Log::DEBUG, "Process move recording");
507 char* fileName = (char*)data;
508 char* newPath = NULL;
510 for (int k = 0; k < length; k++)
514 newPath = (char*)&data[k+1];
518 if (!newPath) return 0;
520 cRecordings Recordings;
521 Recordings.Load(); // probably have to do this
523 cRecording* recording = Recordings.GetByName((char*)fileName);
525 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
529 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
532 log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
533 log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
534 log->log("Client", Log::DEBUG, "to: %s", newPath);
536 const char* t = recording->FileName();
538 char* dateDirName = NULL; int k;
539 char* titleDirName = NULL; int j;
541 // Find the datedirname
542 for(k = strlen(t) - 1; k >= 0; k--)
546 log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
547 dateDirName = new char[strlen(&t[k+1]) + 1];
548 strcpy(dateDirName, &t[k+1]);
553 // Find the titledirname
555 for(j = k-1; j >= 0; j--)
559 log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
560 titleDirName = new char[(k - j - 1) + 1];
561 memcpy(titleDirName, &t[j+1], k - j - 1);
562 titleDirName[k - j - 1] = '\0';
567 log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
568 log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
569 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
571 char* newPathConv = new char[strlen(newPath)+1];
572 strcpy(newPathConv, newPath);
573 ExchangeChars(newPathConv, true);
574 log->log("Client", Log::DEBUG, "EC: %s", newPathConv);
576 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
577 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
578 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
579 delete[] newPathConv;
581 log->log("Client", Log::DEBUG, "%s", newContainer);
584 int statret = stat(newContainer, &dstat);
585 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
587 log->log("Client", Log::DEBUG, "new dir does not exist");
588 int mkdirret = mkdir(newContainer, 0755);
591 delete[] dateDirName;
592 delete[] titleDirName;
593 delete[] newContainer;
597 tcp.sendPacket(rp->getPtr(), rp->getLen());
601 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
603 delete[] dateDirName;
604 delete[] titleDirName;
605 delete[] newContainer;
609 tcp.sendPacket(rp->getPtr(), rp->getLen());
613 // Ok, the directory container has been made, or it pre-existed.
615 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
616 sprintf(newDir, "%s/%s", newContainer, dateDirName);
618 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
619 int renameret = rename(t, newDir);
622 // Success. Test for remove old dir containter
623 char* oldTitleDir = new char[k+1];
624 memcpy(oldTitleDir, t, k);
625 oldTitleDir[k] = '\0';
626 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
627 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
628 delete[] oldTitleDir;
633 #if VDRVERSNUM > 10311
635 ::Recordings.Update();
637 // Success. Send a different packet from just a ulong
638 rp->addULONG(1); // success
639 rp->addString(newDir);
647 tcp.sendPacket(rp->getPtr(), rp->getLen());
649 delete[] dateDirName;
650 delete[] titleDirName;
651 delete[] newContainer;
658 tcp.sendPacket(rp->getPtr(), rp->getLen());
665 tcp.sendPacket(rp->getPtr(), rp->getLen());
671 int MVPClient::processGetChannelsList(UCHAR* data, int length, ResponsePacket* rp)
675 char* chanConfig = config.getValueString("General", "Channels");
677 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
679 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
681 #if VDRVERSNUM < 10300
682 if (!channel->GroupSep() && (!channel->Ca() || allChans))
684 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
687 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
689 if (channel->Vpid()) type = 1;
690 #if VDRVERSNUM < 10300
693 else if (channel->Apid(0)) type = 2;
697 rp->addULONG(channel->Number());
699 rp->addString(channel->Name());
704 tcp.sendPacket(rp->getPtr(), rp->getLen());
706 log->log("Client", Log::DEBUG, "Written channels list");
711 int MVPClient::processGetChannelPids(UCHAR* data, int length, ResponsePacket* rp)
713 ULONG channelNumber = ntohl(*(ULONG*)data);
715 cChannel* channel = channelFromNumber(channelNumber);
720 tcp.sendPacket(rp->getPtr(), rp->getLen());
729 #if VDRVERSNUM < 10300
731 log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
732 log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
734 if (channel->Apid2())
736 else if (channel->Apid1())
743 for (const int *Apid = channel->Apids(); *Apid; Apid++)
747 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
751 for (const int *Spid = channel->Spids(); *Spid; Spid++)
758 // Format of response
777 rp->addULONG(channel->Vpid());
778 rp->addULONG(numApids);
780 #if VDRVERSNUM < 10300
783 rp->addULONG(channel->Apid1());
788 rp->addULONG(channel->Apid2());
794 for (ULONG i = 0; i < numApids; i++)
796 rp->addULONG(channel->Apid(i));
797 rp->addString(channel->Alang(i));
799 rp->addULONG(numDpids);
800 for (ULONG i = 0; i < numDpids; i++)
802 rp->addULONG(channel->Dpid(i));
803 rp->addString(channel->Dlang(i));
805 rp->addULONG(numSpids);
806 for (ULONG i = 0; i < numSpids; i++)
808 rp->addULONG(channel->Spid(i));
809 rp->addString(channel->Slang(i));
812 rp->addULONG(channel->Tpid());
816 tcp.sendPacket(rp->getPtr(), rp->getLen());
818 log->log("Client", Log::DEBUG, "Written channels pids");
823 int MVPClient::processStartStreamingChannel(UCHAR* data, int length, ULONG streamID, ResponsePacket* rp)
827 log->log("Client", Log::ERR, "Client called start streaming twice");
831 log->log("Client", Log::DEBUG, "length = %i", length);
832 ULONG channelNumber = ntohl(*(ULONG*)data);
834 cChannel* channel = channelFromNumber(channelNumber);
839 tcp.sendPacket(rp->getPtr(), rp->getLen());
843 // get the priority we should use
845 int priority = config.getValueLong("General", "Live priority", &fail);
848 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
852 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
857 if (priority < 0) priority = 0;
858 if (priority > 99) priority = 99;
860 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
861 lp = MVPReceiver::create(channel, priority);
867 tcp.sendPacket(rp->getPtr(), rp->getLen());
871 if (!lp->init(&tcp, streamID))
877 tcp.sendPacket(rp->getPtr(), rp->getLen());
883 tcp.sendPacket(rp->getPtr(), rp->getLen());
887 int MVPClient::processStopStreaming(UCHAR* data, int length, ResponsePacket* rp)
889 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
900 delete recordingManager;
902 recordingManager = NULL;
907 tcp.sendPacket(rp->getPtr(), rp->getLen());
911 int MVPClient::processGetBlock(UCHAR* data, int length, ResponsePacket* rp)
913 if (!lp && !recplayer)
915 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
919 ULLONG position = ntohll(*(ULLONG*)data);
920 data += sizeof(ULLONG);
921 ULONG amount = ntohl(*(ULONG*)data);
923 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
925 UCHAR sendBuffer[amount];
926 ULONG amountReceived = 0; // compiler moan.
929 log->log("Client", Log::DEBUG, "getting from live");
930 amountReceived = lp->getBlock(&sendBuffer[0], amount);
934 // vdr has possibly disconnected the receiver
935 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
942 log->log("Client", Log::DEBUG, "getting from recording");
943 amountReceived = recplayer->getBlock(&sendBuffer[0], position, amount);
949 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
953 rp->copyin(sendBuffer, amountReceived);
954 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
958 tcp.sendPacket(rp->getPtr(), rp->getLen());
959 log->log("Client", Log::DEBUG, "Finished getblock, have sent %lu", rp->getLen());
963 int MVPClient::processStartStreamingRecording(UCHAR* data, int length, ResponsePacket* rp)
965 // data is a pointer to the fileName string
967 recordingManager = new cRecordings;
968 recordingManager->Load();
970 cRecording* recording = recordingManager->GetByName((char*)data);
972 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
976 recplayer = new RecPlayer(recording);
978 rp->addULLONG(recplayer->getLengthBytes());
979 rp->addULONG(recplayer->getLengthFrames());
981 tcp.sendPacket(rp->getPtr(), rp->getLen());
983 log->log("Client", Log::DEBUG, "written totalLength");
987 delete recordingManager;
988 recordingManager = NULL;
993 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length, ResponsePacket* rp)
997 ULONG frameNumber = ntohl(*(ULONG*)data);
1002 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1006 retval = recplayer->positionFromFrameNumber(frameNumber);
1009 rp->addULLONG(retval);
1011 tcp.sendPacket(rp->getPtr(), rp->getLen());
1013 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1017 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length, ResponsePacket* rp)
1021 ULLONG position = ntohll(*(ULLONG*)data);
1026 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1030 retval = recplayer->frameNumberFromPosition(position);
1033 rp->addULONG(retval);
1035 tcp.sendPacket(rp->getPtr(), rp->getLen());
1037 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1041 int MVPClient::processGetIFrame(UCHAR* data, int length, ResponsePacket* rp)
1043 bool success = false;
1045 ULONG frameNumber = ntohl(*(ULONG*)data);
1047 ULONG direction = ntohl(*(ULONG*)data);
1050 ULLONG rfilePosition = 0;
1051 ULONG rframeNumber = 0;
1052 ULONG rframeLength = 0;
1056 log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1060 success = recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1063 // returns file position, frame number, length
1067 rp->addULLONG(rfilePosition);
1068 rp->addULONG(rframeNumber);
1069 rp->addULONG(rframeLength);
1077 tcp.sendPacket(rp->getPtr(), rp->getLen());
1079 log->log("Client", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1083 int MVPClient::processGetChannelSchedule(UCHAR* data, int length, ResponsePacket* rp)
1085 ULONG channelNumber = ntohl(*(ULONG*)data);
1087 ULONG startTime = ntohl(*(ULONG*)data);
1089 ULONG duration = ntohl(*(ULONG*)data);
1091 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1093 cChannel* channel = channelFromNumber(channelNumber);
1098 tcp.sendPacket(rp->getPtr(), rp->getLen());
1100 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
1104 log->log("Client", Log::DEBUG, "Got channel");
1106 #if VDRVERSNUM < 10300
1107 cMutexLock MutexLock;
1108 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1110 cSchedulesLock MutexLock;
1111 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1117 tcp.sendPacket(rp->getPtr(), rp->getLen());
1119 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1123 log->log("Client", Log::DEBUG, "Got schedule!s! object");
1125 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1130 tcp.sendPacket(rp->getPtr(), rp->getLen());
1132 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
1136 log->log("Client", Log::DEBUG, "Got schedule object");
1138 const char* empty = "";
1139 bool atLeastOneEvent = false;
1142 ULONG thisEventTime;
1143 ULONG thisEventDuration;
1144 const char* thisEventTitle;
1145 const char* thisEventSubTitle;
1146 const char* thisEventDescription;
1148 #if VDRVERSNUM < 10300
1150 const cEventInfo *event;
1151 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1153 event = Schedule->GetEventNumber(eventNumber);
1155 thisEventID = event->GetEventID();
1156 thisEventTime = event->GetTime();
1157 thisEventDuration = event->GetDuration();
1158 thisEventTitle = event->GetTitle();
1159 thisEventSubTitle = event->GetSubtitle();
1160 thisEventDescription = event->GetExtendedDescription();
1164 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1166 thisEventID = event->EventID();
1167 thisEventTime = event->StartTime();
1168 thisEventDuration = event->Duration();
1169 thisEventTitle = event->Title();
1170 thisEventSubTitle = NULL;
1171 thisEventDescription = event->Description();
1175 log->log("Client", Log::DEBUG, "Got an event object %p", event);
1177 //in the past filter
1178 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1181 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1184 if (thisEventTime >= (startTime + duration)) continue;
1186 if (!thisEventTitle) thisEventTitle = empty;
1187 if (!thisEventSubTitle) thisEventSubTitle = empty;
1188 if (!thisEventDescription) thisEventDescription = empty;
1190 rp->addULONG(thisEventID);
1191 rp->addULONG(thisEventTime);
1192 rp->addULONG(thisEventDuration);
1194 rp->addString(thisEventTitle);
1195 rp->addString(thisEventSubTitle);
1196 rp->addString(thisEventDescription);
1198 atLeastOneEvent = true;
1199 log->log("Client", Log::DEBUG, "Done s3");
1202 log->log("Client", Log::DEBUG, "Got all event data");
1204 if (!atLeastOneEvent)
1207 log->log("Client", Log::DEBUG, "Written 0 because no data");
1211 tcp.sendPacket(rp->getPtr(), rp->getLen());
1213 log->log("Client", Log::DEBUG, "written schedules packet");
1218 #endif //VOMPSTANDALONE
1220 int MVPClient::processConfigSave(UCHAR* buffer, int length, ResponsePacket* rp)
1222 char* section = (char*)buffer;
1226 for (int k = 0; k < length; k++)
1228 if (buffer[k] == '\0')
1232 key = (char*)&buffer[k+1];
1236 value = (char*)&buffer[k+1];
1242 // if the last string (value) doesnt have null terminator, give up
1243 if (buffer[length - 1] != '\0') return 0;
1245 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1246 if (config.setValueString(section, key, value))
1256 tcp.sendPacket(rp->getPtr(), rp->getLen());
1261 int MVPClient::processConfigLoad(UCHAR* buffer, int length, ResponsePacket* rp)
1263 char* section = (char*)buffer;
1266 for (int k = 0; k < length; k++)
1268 if (buffer[k] == '\0')
1270 key = (char*)&buffer[k+1];
1275 char* value = config.getValueString(section, key);
1279 rp->addString(value);
1280 log->log("Client", Log::DEBUG, "Written config load packet");
1286 log->log("Client", Log::DEBUG, "Written config load failed packet");
1290 tcp.sendPacket(rp->getPtr(), rp->getLen());
1295 #ifndef VOMPSTANDALONE
1297 int MVPClient::processGetTimers(UCHAR* buffer, int length, ResponsePacket* rp)
1300 int numTimers = Timers.Count();
1302 rp->addULONG(numTimers);
1304 for (int i = 0; i < numTimers; i++)
1306 timer = Timers.Get(i);
1308 #if VDRVERSNUM < 10300
1309 rp->addULONG(timer->Active());
1311 rp->addULONG(timer->HasFlags(tfActive));
1313 rp->addULONG(timer->Recording());
1314 rp->addULONG(timer->Pending());
1315 rp->addULONG(timer->Priority());
1316 rp->addULONG(timer->Lifetime());
1317 rp->addULONG(timer->Channel()->Number());
1318 rp->addULONG(timer->StartTime());
1319 rp->addULONG(timer->StopTime());
1320 rp->addULONG(timer->Day());
1321 rp->addULONG(timer->WeekDays());
1322 rp->addString(timer->File());
1326 tcp.sendPacket(rp->getPtr(), rp->getLen());
1328 log->log("Client", Log::DEBUG, "Written timers list");
1333 int MVPClient::processSetTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1335 char* timerString = new char[strlen((char*)buffer) + 1];
1336 strcpy(timerString, (char*)buffer);
1338 #if VDRVERSNUM < 10300
1340 // If this is VDR 1.2 the date part of the timer string must be reduced
1341 // to just DD rather than YYYY-MM-DD
1343 int s = 0; // source
1344 int d = 0; // destination
1346 while(c != 2) // copy up to date section, including the second ':'
1348 timerString[d] = buffer[s];
1349 if (buffer[s] == ':') c++;
1353 // now it has copied up to the date section
1355 while(c != 2) // waste YYYY-MM-
1357 if (buffer[s] == '-') c++;
1360 // now source is at the DD
1361 memcpy(&timerString[d], &buffer[s], length - s);
1363 timerString[d] = '\0';
1365 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1366 log->log("Client", Log::DEBUG, "%s", timerString);
1370 cTimer *timer = new cTimer;
1371 if (timer->Parse((char*)timerString))
1373 cTimer *t = Timers.GetTimer(timer);
1377 #if VDRVERSNUM < 10300
1380 Timers.SetModified();
1384 tcp.sendPacket(rp->getPtr(), rp->getLen());
1391 tcp.sendPacket(rp->getPtr(), rp->getLen());
1398 tcp.sendPacket(rp->getPtr(), rp->getLen());
1404 int MVPClient::processDeleteTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1406 log->log("Client", Log::DEBUG, "Delete timer called");
1411 INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4;
1412 INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4;
1413 INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4;
1414 INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4;
1415 INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4;
1418 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1420 if ( (ti->Channel()->Number() == delChannel)
1421 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1422 && (ti->StartTime() == delStart)
1423 && (ti->StopTime() == delStop) )
1431 tcp.sendPacket(rp->getPtr(), rp->getLen());
1435 if (!Timers.BeingEdited())
1437 if (!ti->Recording())
1440 Timers.SetModified();
1443 tcp.sendPacket(rp->getPtr(), rp->getLen());
1448 log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
1451 tcp.sendPacket(rp->getPtr(), rp->getLen());
1457 log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1460 tcp.sendPacket(rp->getPtr(), rp->getLen());
1465 int MVPClient::processGetRecInfo(UCHAR* data, int length, ResponsePacket* rp)
1467 // data is a pointer to the fileName string
1469 cRecordings Recordings;
1470 Recordings.Load(); // probably have to do this
1472 cRecording *recording = Recordings.GetByName((char*)data);
1474 time_t timerStart = 0;
1475 time_t timerStop = 0;
1476 char* summary = NULL;
1477 ULONG resumePoint = 0;
1481 log->log("Client", Log::ERR, "GetRecInfo found no recording");
1484 tcp.sendPacket(rp->getPtr(), rp->getLen());
1489 4 bytes: start time for timer
1490 4 bytes: end time for timer
1491 4 bytes: resume point
1493 4 bytes: num components
1503 // Get current timer
1505 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1508 timerStart = rc->Timer()->StartTime();
1509 timerStop = rc->Timer()->StopTime();
1510 log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1513 rp->addULONG(timerStart);
1514 rp->addULONG(timerStop);
1518 char* value = config.getValueString("ResumeData", (char*)data);
1521 resumePoint = strtoul(value, NULL, 10);
1524 log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1526 rp->addULONG(resumePoint);
1531 #if VDRVERSNUM < 10300
1532 summary = (char*)recording->Summary();
1534 const cRecordingInfo *Info = recording->Info();
1535 summary = (char*)Info->ShortText();
1536 if (isempty(summary)) summary = (char*)Info->Description();
1538 log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1541 rp->addString(summary);
1550 #if VDRVERSNUM < 10300
1552 // Send 0 for numchannels - this signals the client this info is not available
1556 const cComponents* components = Info->Components();
1558 log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1566 rp->addULONG(components->NumComponents());
1568 tComponent* component;
1569 for (int i = 0; i < components->NumComponents(); i++)
1571 component = components->Component(i);
1573 log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1575 rp->addUCHAR(component->stream);
1576 rp->addUCHAR(component->type);
1578 if (component->language)
1580 rp->addString(component->language);
1586 if (component->description)
1588 rp->addString(component->description);
1602 tcp.sendPacket(rp->getPtr(), rp->getLen());
1604 log->log("Client", Log::DEBUG, "Written getrecinfo");
1614 int MVPClient::processReScanRecording(UCHAR* data, int length, ResponsePacket* rp)
1618 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1624 rp->addULLONG(recplayer->getLengthBytes());
1625 rp->addULONG(recplayer->getLengthFrames());
1627 tcp.sendPacket(rp->getPtr(), rp->getLen());
1628 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1632 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1635 int MVPClient::processGetMarks(UCHAR* data, int length, ResponsePacket* rp)
1637 // data is a pointer to the fileName string
1640 cRecordings Recordings;
1641 Recordings.Load(); // probably have to do this
1643 cRecording *recording = Recordings.GetByName((char*)data);
1645 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1649 Marks.Load(recording->FileName());
1652 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1654 log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1656 rp->addULONG(m->position);
1661 log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1667 tcp.sendPacket(rp->getPtr(), rp->getLen());
1669 log->log("Client", Log::DEBUG, "Written Marks list");
1674 #endif //VOMPSTANDALONE
1677 * media List Request:
1679 * 4 VDR_GETMEDIALIST
1680 * 4 flags (currently unused)
1683 * Media List response:
1691 * 4 strlen (incl. 0 Byte)
1696 int MVPClient::processGetMediaList(UCHAR* data, int length, ResponsePacket* rp)
1699 log->log("Client", Log::ERR, "getMediaList packet too short %d", length);
1702 char * dirname=NULL;
1704 //we have a dirname provided
1705 dirname=(char *)&data[4];
1706 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
1709 MediaList * ml=MediaList::readList(baseConfig,dirname);
1711 log->log("Client", Log::ERR, "getMediaList returned NULL");
1715 //response code (not yet set)
1719 rp->addULONG(ml->size());
1721 for (MediaList::iterator nm=ml->begin(); nm<ml->end(); nm++)
1724 log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
1725 rp->addULONG(m->getType());
1727 rp->addULONG(m->getTime());
1730 int len=strlen(m->getFilename());
1732 rp->addULONG(len+1);
1733 rp->addString(m->getFilename());
1738 tcp.sendPacket(rp->getPtr(), rp->getLen());
1740 log->log("Client", Log::DEBUG, "Written Media list");
1745 * get image Request:
1746 * 4 flags (currently unused)
1751 * get image response:
1757 int MVPClient::processGetPicture(UCHAR* data, int length, ResponsePacket* rp)
1760 log->log("Client", Log::ERR, "getPicture packet too short %d", length);
1767 char * filename=NULL;
1769 //we have a dirname provided
1770 filename=(char *)&data[12];
1771 log->log("Client", Log::DEBUG, "getPicture %s", filename);
1774 log->log("Client", Log::ERR, "getPicture empty filename");
1777 imageFile=fopen(filename,"r");
1778 if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename);
1783 if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size;
1785 //response code (not yet set)
1790 log->log("Client", Log::DEBUG, "getPicture size %u", size);
1793 tcp.sendPacket(rp->getPtr(), rp->getLen());
1795 log->log("Client", Log::DEBUG, "Written getPicture");
1801 int MVPClient::processGetImageBlock(UCHAR* data, int length, ResponsePacket* rp)
1805 log->log("Client", Log::DEBUG, "Get image block called when no image active");
1809 ULLONG position = ntohll(*(ULLONG*)data);
1810 data += sizeof(ULLONG);
1811 ULONG amount = ntohl(*(ULONG*)data);
1813 log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
1815 UCHAR sendBuffer[amount];
1816 ULONG amountReceived = 0; // compiler moan.
1817 ULLONG cpos=ftell(imageFile);
1818 if (position != cpos) {
1819 fseek(imageFile,position-cpos,SEEK_CUR);
1821 if (position != (ULLONG)ftell(imageFile)) {
1822 log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position);
1825 amountReceived=fread(&sendBuffer[0],1,amount,imageFile);
1828 if (!amountReceived)
1831 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
1835 rp->copyin(sendBuffer, amount);
1836 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
1840 tcp.sendPacket(rp->getPtr(), rp->getLen());
1846 int MVPClient::processGetLanguageList(UCHAR* data, int length, ResponsePacket* rp)
1848 i18n.findLanguages();
1849 const I18n::lang_code_list& languages = i18n.getLanguageList();
1851 I18n::lang_code_list::const_iterator iter;
1852 for (iter = languages.begin(); iter != languages.end(); ++iter)
1854 rp->addString(iter->first.c_str());
1855 rp->addString(iter->second.c_str());
1858 tcp.sendPacket(rp->getPtr(), rp->getLen());
1862 int MVPClient::processGetLanguageContent(UCHAR* data, int length, ResponsePacket* rp)
1864 if (length <= 0) return 0;
1865 std::string code, result;
1866 code.assign((char*)data, length - 1);
1867 i18n.findLanguages();
1868 I18n::trans_table texts = i18n.getLanguageContent(code);
1869 I18n::trans_table::const_iterator iter;
1870 for (iter = texts.begin(); iter != texts.end(); ++iter)
1872 rp->addString(iter->first.c_str());
1873 rp->addString(iter->second.c_str());
1876 tcp.sendPacket(rp->getPtr(), rp->getLen());
1894 event = Schedule->GetPresentEvent();
1896 fprintf(f, "\n\nCurrent event\n\n");
1898 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1899 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1900 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1901 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1903 event = Schedule->GetFollowingEvent();
1905 fprintf(f, "\n\nFollowing event\n\n");
1907 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1908 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1909 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1910 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1914 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1915 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1916 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1917 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1923 void MVPClient::test2()
1925 FILE* f = fopen("/tmp/s.txt", "w");
1927 #if VDRVERSNUM < 10300
1928 cMutexLock MutexLock;
1929 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1931 cSchedulesLock MutexLock;
1932 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1937 fprintf(f, "Schedules = NULL\n");
1942 fprintf(f, "Schedules dump:\n");
1946 const cSchedule *Schedule;
1947 int scheduleNumber = 0;
1950 cChannel *thisChannel;
1952 #if VDRVERSNUM < 10300
1953 const cEventInfo *event;
1954 int eventNumber = 0;
1956 const cEvent *event;
1959 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1960 // Schedule = Schedules->GetSchedule();
1961 Schedule = Schedules->First();
1964 fprintf(f, "First Schedule = NULL\n");
1971 fprintf(f, "Schedule #%i\n", scheduleNumber);
1972 fprintf(f, "-------------\n\n");
1974 #if VDRVERSNUM < 10300
1975 tchid = Schedule->GetChannelID();
1977 tchid = Schedule->ChannelID();
1980 #if VDRVERSNUM < 10300
1981 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1982 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1984 // put the count at the end.
1987 thisChannel = Channels.GetByChannelID(tchid, true);
1990 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1994 fprintf(f, "thisChannel = NULL for tchid\n");
1997 #if VDRVERSNUM < 10300
1998 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
2000 event = Schedule->GetEventNumber(eventNumber);
2001 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
2002 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
2003 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
2004 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
2005 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
2006 fprintf(f, "Event %u dump:\n", eventNumber);
2011 // This whole section needs rewriting to walk the list.
2012 event = Schedule->Events()->First();
2014 event = Schedule->Events()->Next(event);
2019 fprintf(f, "\nDump from object:\n");
2021 fprintf(f, "\nEND\n");
2026 fprintf(f, "End of current Schedule\n\n\n");
2028 Schedule = (const cSchedule *)Schedules->Next(Schedule);
2036 const cEventInfo *GetPresentEvent(void) const;
2037 const cEventInfo *GetFollowingEvent(void) const;
2038 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
2039 const cEventInfo *GetEventAround(time_t tTime) const;
2040 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
2043 const unsigned char GetTableID(void) const;
2044 const char *GetTimeString(void) const;
2045 const char *GetEndTimeString(void) const;
2046 const char *GetDate(void) const;
2047 bool IsFollowing(void) const;
2048 bool IsPresent(void) const;
2049 const char *GetExtendedDescription(void) const;
2050 const char *GetSubtitle(void) const;
2051 const char *GetTitle(void) const;
2052 unsigned short GetEventID(void) const;
2053 long GetDuration(void) const;
2054 time_t GetTime(void) const;
2055 tChannelID GetChannelID(void) const;
2056 int GetChannelNumber(void) const { return nChannelNumber; }
2057 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
2058 void Dump(FILE *f, const char *Prefix = "") const;
2062 void MVPClient::test(int channelNumber)
2064 FILE* f = fopen("/tmp/test.txt", "w");
2066 cMutexLock MutexLock;
2067 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
2071 fprintf(f, "Schedules = NULL\n");
2076 fprintf(f, "Schedules dump:\n");
2077 // Schedules->Dump(f);
2079 const cSchedule *Schedule;
2080 cChannel *thisChannel;
2081 const cEventInfo *event;
2083 thisChannel = channelFromNumber(channelNumber);
2086 fprintf(f, "thisChannel = NULL\n");
2091 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
2092 // Schedule = Schedules->GetSchedule();
2093 // Schedule = Schedules->First();
2096 fprintf(f, "First Schedule = NULL\n");
2101 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
2103 // For some channels VDR seems to pick a random point in time to
2104 // start dishing out events, but they are in order
2105 // at some point in the list the time snaps to the current event
2110 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
2112 event = Schedule->GetEventNumber(eventNumber);
2113 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
2114 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
2115 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
2119 fprintf(f, "\nEND\n");
2128 Schedules = the collection of all the Schedule objects
2129 Schedule = One schedule, contants all the events for a channel
2130 Event = One programme
2139 Subtitle (used for "Programmes resume at ...")
2142 IsPresent ? easy to work out tho. Oh it doesn't always work
2145 void MVPClient::test2()
2147 log->log("-", Log::DEBUG, "Timers List");
2149 for (int i = 0; i < Timers.Count(); i++)
2151 cTimer *timer = Timers.Get(i);
2152 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
2153 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
2154 #if VDRVERSNUM < 10300
2155 log->log("-", Log::DEBUG, "active=%i recording=%i pending=%i start=%li stop=%li priority=%i lifetime=%i", timer->Active(), timer->Recording(), timer->Pending(), timer->StartTime(), timer->StopTime(), timer->Priority(), timer->Lifetime());
2157 log->log("-", Log::DEBUG, "active=%i recording=%i pending=%i start=%li stop=%li priority=%i lifetime=%i", timer->HasFlags(tfActive), timer->Recording(), timer->Pending(), timer->StartTime(), timer->StopTime(), timer->Priority(), timer->Lifetime());
2159 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
2160 log->log("-", Log::DEBUG, "");
2163 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
2164 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
2165 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
2169 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
2170 recording is a bool, 0 for not currently recording, 1 for currently recording
2171 pending is a bool, 0 for would not be trying to record this right now, 1 for would/is trying to record this right now