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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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;
237 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");
309 result = processLogin(data, extraDataLength, rp);
311 #ifndef VOMPSTANDALONE
313 result = processGetRecordingsList(data, extraDataLength, rp);
316 result = processDeleteRecording(data, extraDataLength, rp);
319 result = processGetChannelsList(data, extraDataLength, rp);
322 result = processStartStreamingChannel(data, extraDataLength, requestID, rp);
325 result = processGetBlock(data, extraDataLength, rp);
328 result = processStopStreaming(data, extraDataLength, rp);
331 result = processStartStreamingRecording(data, extraDataLength, rp);
335 result = processGetChannelSchedule(data, extraDataLength, rp);
338 result = processConfigSave(data, extraDataLength, rp);
341 result = processConfigLoad(data, extraDataLength, rp);
343 #ifndef VOMPSTANDALONE
345 result = processReScanRecording(data, extraDataLength, rp); // FIXME obselete
348 result = processGetTimers(data, extraDataLength, rp);
351 result = processSetTimer(data, extraDataLength, rp);
354 result = processPositionFromFrameNumber(data, extraDataLength, rp);
357 result = processFrameNumberFromPosition(data, extraDataLength, rp);
360 result = processMoveRecording(data, extraDataLength, rp);
363 result = processGetIFrame(data, extraDataLength, rp);
366 result = processGetRecInfo(data, extraDataLength, rp);
369 result = processGetMarks(data, extraDataLength, rp);
372 result = processGetChannelPids(data, extraDataLength, rp);
375 result = processDeleteTimer(data, extraDataLength, rp);
379 result = processGetMediaList(data, extraDataLength, rp);
382 result = processGetPicture(data, extraDataLength, rp);
385 result = processGetImageBlock(data, extraDataLength, rp);
388 result = processGetLanguageList(data, extraDataLength, rp);
391 result = processGetLanguageContent(data, extraDataLength, rp);
395 if (data) free(data);
400 int MVPClient::processLogin(UCHAR* buffer, int length, ResponsePacket* rp)
402 if (length != 6) return 0;
406 char configFileName[PATH_MAX];
407 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]);
408 config.init(configFileName);
410 // Send the login reply
412 time_t timeNow = time(NULL);
413 struct tm* timeStruct = localtime(&timeNow);
414 int timeOffset = timeStruct->tm_gmtoff;
416 rp->addULONG(timeNow);
417 rp->addLONG(timeOffset);
419 tcp.sendPacket(rp->getPtr(), rp->getLen());
420 log->log("Client", Log::DEBUG, "written login reply len %lu", rp->getLen());
427 #ifndef VOMPSTANDALONE
428 int MVPClient::processGetRecordingsList(UCHAR* data, int length, ResponsePacket* rp)
431 int Percent = VideoDiskSpace(&FreeMB);
432 int Total = (FreeMB / (100 - Percent)) * 100;
435 rp->addULONG(FreeMB);
436 rp->addULONG(Percent);
438 cRecordings Recordings;
441 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
443 rp->addULONG(recording->start);
444 rp->addString(recording->Name());
445 rp->addString(recording->FileName());
449 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());
505 int MVPClient::processMoveRecording(UCHAR* data, int length, ResponsePacket* rp)
507 log->log("Client", Log::DEBUG, "Process move recording");
508 char* fileName = (char*)data;
509 char* newPath = NULL;
511 for (int k = 0; k < length; k++)
515 newPath = (char*)&data[k+1];
519 if (!newPath) return 0;
521 cRecordings Recordings;
522 Recordings.Load(); // probably have to do this
524 cRecording* recording = Recordings.GetByName((char*)fileName);
526 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
530 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
533 log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
534 log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
535 log->log("Client", Log::DEBUG, "to: %s", newPath);
537 const char* t = recording->FileName();
539 char* dateDirName = NULL; int k;
540 char* titleDirName = NULL; int j;
542 // Find the datedirname
543 for(k = strlen(t) - 1; k >= 0; k--)
547 log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
548 dateDirName = new char[strlen(&t[k+1]) + 1];
549 strcpy(dateDirName, &t[k+1]);
554 // Find the titledirname
556 for(j = k-1; j >= 0; j--)
560 log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
561 titleDirName = new char[(k - j - 1) + 1];
562 memcpy(titleDirName, &t[j+1], k - j - 1);
563 titleDirName[k - j - 1] = '\0';
568 log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
569 log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
571 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
573 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
574 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
575 sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
577 // FIXME Check whether this already exists before mkdiring it
579 log->log("Client", Log::DEBUG, "%s", newContainer);
583 int statret = stat(newContainer, &dstat);
584 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
586 log->log("Client", Log::DEBUG, "new dir does not exist");
587 int mkdirret = mkdir(newContainer, 0755);
590 delete[] dateDirName;
591 delete[] titleDirName;
592 delete[] newContainer;
596 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());
614 // Ok, the directory container has been made, or it pre-existed.
616 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
617 sprintf(newDir, "%s/%s", newContainer, dateDirName);
619 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
620 int renameret = rename(t, newDir);
623 // Success. Test for remove old dir containter
624 char* oldTitleDir = new char[k+1];
625 memcpy(oldTitleDir, t, k);
626 oldTitleDir[k] = '\0';
627 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
628 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
629 delete[] oldTitleDir;
634 #if VDRVERSNUM > 10311
636 ::Recordings.Update();
638 // Success. Send a different packet from just a ulong
639 rp->addULONG(1); // success
640 rp->addString(newDir);
648 tcp.sendPacket(rp->getPtr(), rp->getLen());
651 delete[] dateDirName;
652 delete[] titleDirName;
653 delete[] newContainer;
660 tcp.sendPacket(rp->getPtr(), rp->getLen());
668 tcp.sendPacket(rp->getPtr(), rp->getLen());
675 int MVPClient::processGetChannelsList(UCHAR* data, int length, ResponsePacket* rp)
679 char* chanConfig = config.getValueString("General", "Channels");
681 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
683 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
685 #if VDRVERSNUM < 10300
686 if (!channel->GroupSep() && (!channel->Ca() || allChans))
688 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
691 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
693 if (channel->Vpid()) type = 1;
694 #if VDRVERSNUM < 10300
697 else if (channel->Apid(0)) type = 2;
701 rp->addULONG(channel->Number());
703 rp->addString(channel->Name());
708 tcp.sendPacket(rp->getPtr(), rp->getLen());
711 log->log("Client", Log::DEBUG, "Written channels list");
716 int MVPClient::processGetChannelPids(UCHAR* data, int length, ResponsePacket* rp)
718 ULONG channelNumber = ntohl(*(ULONG*)data);
720 cChannel* channel = channelFromNumber(channelNumber);
725 tcp.sendPacket(rp->getPtr(), rp->getLen());
732 #if VDRVERSNUM < 10300
734 log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
735 log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
737 if (channel->Apid2())
739 else if (channel->Apid1())
746 for (const int *Apid = channel->Apids(); *Apid; Apid++)
753 // Format of response
761 rp->addULONG(channel->Vpid());
762 rp->addULONG(numApids);
764 #if VDRVERSNUM < 10300
767 rp->addULONG(channel->Apid1());
772 rp->addULONG(channel->Apid2());
776 for (ULONG i = 0; i < numApids; i++)
778 rp->addULONG(channel->Apid(i));
779 rp->addString(channel->Alang(i));
784 tcp.sendPacket(rp->getPtr(), rp->getLen());
787 log->log("Client", Log::DEBUG, "Written channels pids");
792 int MVPClient::processStartStreamingChannel(UCHAR* data, int length, ULONG streamID, ResponsePacket* rp)
796 log->log("Client", Log::ERR, "Client called start streaming twice");
800 log->log("Client", Log::DEBUG, "length = %i", length);
801 ULONG channelNumber = ntohl(*(ULONG*)data);
803 cChannel* channel = channelFromNumber(channelNumber);
808 tcp.sendPacket(rp->getPtr(), rp->getLen());
813 // get the priority we should use
815 int priority = config.getValueLong("General", "Live priority", &fail);
818 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
822 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
827 if (priority < 0) priority = 0;
828 if (priority > 99) priority = 99;
830 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
831 lp = MVPReceiver::create(channel, priority);
837 tcp.sendPacket(rp->getPtr(), rp->getLen());
842 if (!lp->init(&tcp, streamID))
848 tcp.sendPacket(rp->getPtr(), rp->getLen());
855 tcp.sendPacket(rp->getPtr(), rp->getLen());
860 int MVPClient::processStopStreaming(UCHAR* data, int length, ResponsePacket* rp)
862 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
873 delete recordingManager;
875 recordingManager = NULL;
880 tcp.sendPacket(rp->getPtr(), rp->getLen());
885 int MVPClient::processGetBlock(UCHAR* data, int length, ResponsePacket* rp)
887 if (!lp && !recplayer)
889 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
893 ULLONG position = ntohll(*(ULLONG*)data);
894 data += sizeof(ULLONG);
895 ULONG amount = ntohl(*(ULONG*)data);
897 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
899 UCHAR sendBuffer[amount];
900 ULONG amountReceived = 0; // compiler moan.
903 log->log("Client", Log::DEBUG, "getting from live");
904 amountReceived = lp->getBlock(&sendBuffer[0], amount);
908 // vdr has possibly disconnected the receiver
909 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
916 log->log("Client", Log::DEBUG, "getting from recording");
917 amountReceived = recplayer->getBlock(&sendBuffer[0], position, amount);
923 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
927 rp->copyin(sendBuffer, amountReceived);
928 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
932 tcp.sendPacket(rp->getPtr(), rp->getLen());
933 log->log("Client", Log::DEBUG, "Finished getblock, have sent %lu", rp->getLen());
938 int MVPClient::processStartStreamingRecording(UCHAR* data, int length, ResponsePacket* rp)
940 // data is a pointer to the fileName string
942 recordingManager = new cRecordings;
943 recordingManager->Load();
945 cRecording* recording = recordingManager->GetByName((char*)data);
947 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
951 recplayer = new RecPlayer(recording);
953 rp->addULLONG(recplayer->getLengthBytes());
954 rp->addULONG(recplayer->getLengthFrames());
956 tcp.sendPacket(rp->getPtr(), rp->getLen());
959 log->log("Client", Log::DEBUG, "written totalLength");
963 delete recordingManager;
964 recordingManager = NULL;
969 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length, ResponsePacket* rp)
973 ULONG frameNumber = ntohl(*(ULONG*)data);
978 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
982 retval = recplayer->positionFromFrameNumber(frameNumber);
985 rp->addULLONG(retval);
987 tcp.sendPacket(rp->getPtr(), rp->getLen());
990 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
994 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length, ResponsePacket* rp)
998 ULLONG position = ntohll(*(ULLONG*)data);
1003 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1007 retval = recplayer->frameNumberFromPosition(position);
1010 rp->addULONG(retval);
1012 tcp.sendPacket(rp->getPtr(), rp->getLen());
1015 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1019 int MVPClient::processGetIFrame(UCHAR* data, int length, ResponsePacket* rp)
1021 bool success = false;
1023 ULONG frameNumber = ntohl(*(ULONG*)data);
1025 ULONG direction = ntohl(*(ULONG*)data);
1028 ULLONG rfilePosition = 0;
1029 ULONG rframeNumber = 0;
1030 ULONG rframeLength = 0;
1034 log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1038 success = recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1041 // returns file position, frame number, length
1045 rp->addULLONG(rfilePosition);
1046 rp->addULONG(rframeNumber);
1047 rp->addULONG(rframeLength);
1055 tcp.sendPacket(rp->getPtr(), rp->getLen());
1058 log->log("Client", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1062 int MVPClient::processGetChannelSchedule(UCHAR* data, int length, ResponsePacket* rp)
1064 ULONG channelNumber = ntohl(*(ULONG*)data);
1066 ULONG startTime = ntohl(*(ULONG*)data);
1068 ULONG duration = ntohl(*(ULONG*)data);
1070 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1072 cChannel* channel = channelFromNumber(channelNumber);
1077 tcp.sendPacket(rp->getPtr(), rp->getLen());
1080 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
1084 log->log("Client", Log::DEBUG, "Got channel");
1086 #if VDRVERSNUM < 10300
1087 cMutexLock MutexLock;
1088 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1090 cSchedulesLock MutexLock;
1091 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1097 tcp.sendPacket(rp->getPtr(), rp->getLen());
1100 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1104 log->log("Client", Log::DEBUG, "Got schedule!s! object");
1106 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1111 tcp.sendPacket(rp->getPtr(), rp->getLen());
1114 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
1118 log->log("Client", Log::DEBUG, "Got schedule object");
1120 const char* empty = "";
1121 bool atLeastOneEvent = false;
1124 ULONG thisEventTime;
1125 ULONG thisEventDuration;
1126 const char* thisEventTitle;
1127 const char* thisEventSubTitle;
1128 const char* thisEventDescription;
1130 #if VDRVERSNUM < 10300
1132 const cEventInfo *event;
1133 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1135 event = Schedule->GetEventNumber(eventNumber);
1137 thisEventID = event->GetEventID();
1138 thisEventTime = event->GetTime();
1139 thisEventDuration = event->GetDuration();
1140 thisEventTitle = event->GetTitle();
1141 thisEventSubTitle = event->GetSubtitle();
1142 thisEventDescription = event->GetExtendedDescription();
1146 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1148 thisEventID = event->EventID();
1149 thisEventTime = event->StartTime();
1150 thisEventDuration = event->Duration();
1151 thisEventTitle = event->Title();
1152 thisEventSubTitle = NULL;
1153 thisEventDescription = event->Description();
1157 log->log("Client", Log::DEBUG, "Got an event object %p", event);
1159 //in the past filter
1160 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1163 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1166 if (thisEventTime >= (startTime + duration)) continue;
1168 if (!thisEventTitle) thisEventTitle = empty;
1169 if (!thisEventSubTitle) thisEventSubTitle = empty;
1170 if (!thisEventDescription) thisEventDescription = empty;
1172 rp->addULONG(thisEventID);
1173 rp->addULONG(thisEventTime);
1174 rp->addULONG(thisEventDuration);
1176 rp->addString(thisEventTitle);
1177 rp->addString(thisEventSubTitle);
1178 rp->addString(thisEventDescription);
1180 atLeastOneEvent = true;
1181 log->log("Client", Log::DEBUG, "Done s3");
1184 log->log("Client", Log::DEBUG, "Got all event data");
1186 if (!atLeastOneEvent)
1189 log->log("Client", Log::DEBUG, "Written 0 because no data");
1193 tcp.sendPacket(rp->getPtr(), rp->getLen());
1196 log->log("Client", Log::DEBUG, "written schedules packet");
1201 #endif //VOMPSTANDALONE
1203 int MVPClient::processConfigSave(UCHAR* buffer, int length, ResponsePacket* rp)
1205 char* section = (char*)buffer;
1209 for (int k = 0; k < length; k++)
1211 if (buffer[k] == '\0')
1215 key = (char*)&buffer[k+1];
1219 value = (char*)&buffer[k+1];
1225 // if the last string (value) doesnt have null terminator, give up
1226 if (buffer[length - 1] != '\0') return 0;
1228 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1229 if (config.setValueString(section, key, value))
1239 tcp.sendPacket(rp->getPtr(), rp->getLen());
1245 int MVPClient::processConfigLoad(UCHAR* buffer, int length, ResponsePacket* rp)
1247 char* section = (char*)buffer;
1250 for (int k = 0; k < length; k++)
1252 if (buffer[k] == '\0')
1254 key = (char*)&buffer[k+1];
1259 char* value = config.getValueString(section, key);
1263 rp->addString(value);
1264 log->log("Client", Log::DEBUG, "Written config load packet");
1270 log->log("Client", Log::DEBUG, "Written config load failed packet");
1274 tcp.sendPacket(rp->getPtr(), rp->getLen());
1280 #ifndef VOMPSTANDALONE
1282 int MVPClient::processGetTimers(UCHAR* buffer, int length, ResponsePacket* rp)
1285 int numTimers = Timers.Count();
1287 rp->addULONG(numTimers);
1289 for (int i = 0; i < numTimers; i++)
1291 timer = Timers.Get(i);
1293 #if VDRVERSNUM < 10300
1294 rp->addULONG(timer->Active());
1296 rp->addULONG(timer->HasFlags(tfActive));
1298 rp->addULONG(timer->Recording());
1299 rp->addULONG(timer->Pending());
1300 rp->addULONG(timer->Priority());
1301 rp->addULONG(timer->Lifetime());
1302 rp->addULONG(timer->Channel()->Number());
1303 rp->addULONG(timer->StartTime());
1304 rp->addULONG(timer->StopTime());
1305 rp->addULONG(timer->Day());
1306 rp->addULONG(timer->WeekDays());
1307 rp->addString(timer->File());
1311 tcp.sendPacket(rp->getPtr(), rp->getLen());
1314 log->log("Client", Log::DEBUG, "Written timers list");
1319 int MVPClient::processSetTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1321 char* timerString = new char[strlen((char*)buffer) + 1];
1322 strcpy(timerString, (char*)buffer);
1324 #if VDRVERSNUM < 10300
1326 // If this is VDR 1.2 the date part of the timer string must be reduced
1327 // to just DD rather than YYYY-MM-DD
1329 int s = 0; // source
1330 int d = 0; // destination
1332 while(c != 2) // copy up to date section, including the second ':'
1334 timerString[d] = buffer[s];
1335 if (buffer[s] == ':') c++;
1339 // now it has copied up to the date section
1341 while(c != 2) // waste YYYY-MM-
1343 if (buffer[s] == '-') c++;
1346 // now source is at the DD
1347 memcpy(&timerString[d], &buffer[s], length - s);
1349 timerString[d] = '\0';
1351 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1352 log->log("Client", Log::DEBUG, "%s", timerString);
1356 cTimer *timer = new cTimer;
1357 if (timer->Parse((char*)timerString))
1359 cTimer *t = Timers.GetTimer(timer);
1363 #if VDRVERSNUM < 10300
1366 Timers.SetModified();
1370 tcp.sendPacket(rp->getPtr(), rp->getLen());
1378 tcp.sendPacket(rp->getPtr(), rp->getLen());
1386 tcp.sendPacket(rp->getPtr(), rp->getLen());
1393 int MVPClient::processDeleteTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1395 log->log("Client", Log::DEBUG, "Delete timer called");
1400 INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4;
1401 INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4;
1402 INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4;
1403 INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4;
1404 INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4;
1407 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1409 if ( (ti->Channel()->Number() == delChannel)
1410 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1411 && (ti->StartTime() == delStart)
1412 && (ti->StopTime() == delStop) )
1420 tcp.sendPacket(rp->getPtr(), rp->getLen());
1425 if (!Timers.BeingEdited())
1427 if (!ti->Recording())
1430 Timers.SetModified();
1433 tcp.sendPacket(rp->getPtr(), rp->getLen());
1439 log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
1442 tcp.sendPacket(rp->getPtr(), rp->getLen());
1449 log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1452 tcp.sendPacket(rp->getPtr(), rp->getLen());
1458 int MVPClient::processGetRecInfo(UCHAR* data, int length, ResponsePacket* rp)
1460 // data is a pointer to the fileName string
1462 cRecordings Recordings;
1463 Recordings.Load(); // probably have to do this
1465 cRecording *recording = Recordings.GetByName((char*)data);
1467 time_t timerStart = 0;
1468 time_t timerStop = 0;
1469 char* summary = NULL;
1470 ULONG resumePoint = 0;
1474 log->log("Client", Log::ERR, "GetRecInfo found no recording");
1477 tcp.sendPacket(rp->getPtr(), rp->getLen());
1483 4 bytes: start time for timer
1484 4 bytes: end time for timer
1485 4 bytes: resume point
1487 4 bytes: num components
1497 // Get current timer
1499 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1502 timerStart = rc->Timer()->StartTime();
1503 timerStop = rc->Timer()->StopTime();
1504 log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1507 rp->addULONG(timerStart);
1508 rp->addULONG(timerStop);
1512 char* value = config.getValueString("ResumeData", (char*)data);
1515 resumePoint = strtoul(value, NULL, 10);
1518 log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1520 rp->addULONG(resumePoint);
1525 #if VDRVERSNUM < 10300
1526 summary = (char*)recording->Summary();
1528 const cRecordingInfo *Info = recording->Info();
1529 summary = (char*)Info->ShortText();
1530 if (isempty(summary)) summary = (char*)Info->Description();
1532 log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1535 rp->addString(summary);
1544 #if VDRVERSNUM < 10300
1546 // Send 0 for numchannels - this signals the client this info is not available
1550 const cComponents* components = Info->Components();
1552 log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1560 rp->addULONG(components->NumComponents());
1562 tComponent* component;
1563 for (int i = 0; i < components->NumComponents(); i++)
1565 component = components->Component(i);
1567 log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1569 rp->addUCHAR(component->stream);
1570 rp->addUCHAR(component->type);
1572 if (component->language)
1574 rp->addString(component->language);
1580 if (component->description)
1582 rp->addString(component->description);
1596 tcp.sendPacket(rp->getPtr(), rp->getLen());
1599 log->log("Client", Log::DEBUG, "Written getrecinfo");
1609 int MVPClient::processReScanRecording(UCHAR* data, int length, ResponsePacket* rp)
1613 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1619 rp->addULLONG(recplayer->getLengthBytes());
1620 rp->addULONG(recplayer->getLengthFrames());
1622 tcp.sendPacket(rp->getPtr(), rp->getLen());
1624 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1628 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1631 int MVPClient::processGetMarks(UCHAR* data, int length, ResponsePacket* rp)
1633 // data is a pointer to the fileName string
1636 cRecordings Recordings;
1637 Recordings.Load(); // probably have to do this
1639 cRecording *recording = Recordings.GetByName((char*)data);
1641 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1645 Marks.Load(recording->FileName());
1648 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1650 log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1652 rp->addULONG(m->position);
1657 log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1663 tcp.sendPacket(rp->getPtr(), rp->getLen());
1666 log->log("Client", Log::DEBUG, "Written Marks list");
1671 #endif //VOMPSTANDALONE
1674 * media List Request:
1676 * 4 VDR_GETMEDIALIST
1677 * 4 flags (currently unused)
1680 * Media List response:
1688 * 4 strlen (incl. 0 Byte)
1693 int MVPClient::processGetMediaList(UCHAR* data, int length, ResponsePacket* rp)
1696 log->log("Client", Log::ERR, "getMediaList packet too short %d", length);
1699 char * dirname=NULL;
1701 //we have a dirname provided
1702 dirname=(char *)&data[4];
1703 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
1706 MediaList * ml=MediaList::readList(baseConfig,dirname);
1708 log->log("Client", Log::ERR, "getMediaList returned NULL");
1712 //response code (not yet set)
1716 rp->addULONG(ml->size());
1718 for (MediaList::iterator nm=ml->begin(); nm<ml->end(); nm++)
1721 log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
1722 rp->addULONG(m->getType());
1724 rp->addULONG(m->getTime());
1727 int len=strlen(m->getFilename());
1729 rp->addULONG(len+1);
1730 rp->addString(m->getFilename());
1735 tcp.sendPacket(rp->getPtr(), rp->getLen());
1738 log->log("Client", Log::DEBUG, "Written Media list");
1743 * get image Request:
1744 * 4 flags (currently unused)
1749 * get image response:
1755 int MVPClient::processGetPicture(UCHAR* data, int length, ResponsePacket* rp)
1758 log->log("Client", Log::ERR, "getPicture packet too short %d", length);
1765 char * filename=NULL;
1767 //we have a dirname provided
1768 filename=(char *)&data[12];
1769 log->log("Client", Log::DEBUG, "getPicture %s", filename);
1772 log->log("Client", Log::ERR, "getPicture empty filename");
1775 imageFile=fopen(filename,"r");
1776 if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename);
1781 if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size;
1783 //response code (not yet set)
1788 log->log("Client", Log::DEBUG, "getPicture size %u", size);
1791 tcp.sendPacket(rp->getPtr(), rp->getLen());
1794 log->log("Client", Log::DEBUG, "Written getPicture");
1800 int MVPClient::processGetImageBlock(UCHAR* data, int length, ResponsePacket* rp)
1804 log->log("Client", Log::DEBUG, "Get image block called when no image active");
1808 ULLONG position = ntohll(*(ULLONG*)data);
1809 data += sizeof(ULLONG);
1810 ULONG amount = ntohl(*(ULONG*)data);
1812 log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
1814 UCHAR sendBuffer[amount];
1815 ULONG amountReceived = 0; // compiler moan.
1816 ULLONG cpos=ftell(imageFile);
1817 if (position != cpos) {
1818 fseek(imageFile,position-cpos,SEEK_CUR);
1820 if (position != (ULLONG)ftell(imageFile)) {
1821 log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position);
1824 amountReceived=fread(&sendBuffer[0],1,amount,imageFile);
1827 if (!amountReceived)
1830 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
1834 rp->copyin(sendBuffer, amount);
1835 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
1839 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());
1863 int MVPClient::processGetLanguageContent(UCHAR* data, int length, ResponsePacket* rp)
1865 if (length <= 0) return 0;
1866 std::string code, result;
1867 code.assign((char*)data, length - 1);
1868 i18n.findLanguages();
1869 I18n::trans_table texts = i18n.getLanguageContent(code);
1870 I18n::trans_table::const_iterator iter;
1871 for (iter = texts.begin(); iter != texts.end(); ++iter)
1873 rp->addString(iter->first.c_str());
1874 rp->addString(iter->second.c_str());
1877 tcp.sendPacket(rp->getPtr(), rp->getLen());
1896 event = Schedule->GetPresentEvent();
1898 fprintf(f, "\n\nCurrent event\n\n");
1900 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1901 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1902 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1903 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1905 event = Schedule->GetFollowingEvent();
1907 fprintf(f, "\n\nFollowing event\n\n");
1909 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1910 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1911 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1912 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1916 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1917 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1918 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1919 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1925 void MVPClient::test2()
1927 FILE* f = fopen("/tmp/s.txt", "w");
1929 #if VDRVERSNUM < 10300
1930 cMutexLock MutexLock;
1931 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1933 cSchedulesLock MutexLock;
1934 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1939 fprintf(f, "Schedules = NULL\n");
1944 fprintf(f, "Schedules dump:\n");
1948 const cSchedule *Schedule;
1949 int scheduleNumber = 0;
1952 cChannel *thisChannel;
1954 #if VDRVERSNUM < 10300
1955 const cEventInfo *event;
1956 int eventNumber = 0;
1958 const cEvent *event;
1961 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1962 // Schedule = Schedules->GetSchedule();
1963 Schedule = Schedules->First();
1966 fprintf(f, "First Schedule = NULL\n");
1973 fprintf(f, "Schedule #%i\n", scheduleNumber);
1974 fprintf(f, "-------------\n\n");
1976 #if VDRVERSNUM < 10300
1977 tchid = Schedule->GetChannelID();
1979 tchid = Schedule->ChannelID();
1982 #if VDRVERSNUM < 10300
1983 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1984 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1986 // put the count at the end.
1989 thisChannel = Channels.GetByChannelID(tchid, true);
1992 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1996 fprintf(f, "thisChannel = NULL for tchid\n");
1999 #if VDRVERSNUM < 10300
2000 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
2002 event = Schedule->GetEventNumber(eventNumber);
2003 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
2004 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
2005 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
2006 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
2007 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
2008 fprintf(f, "Event %u dump:\n", eventNumber);
2013 // This whole section needs rewriting to walk the list.
2014 event = Schedule->Events()->First();
2016 event = Schedule->Events()->Next(event);
2021 fprintf(f, "\nDump from object:\n");
2023 fprintf(f, "\nEND\n");
2028 fprintf(f, "End of current Schedule\n\n\n");
2030 Schedule = (const cSchedule *)Schedules->Next(Schedule);
2038 const cEventInfo *GetPresentEvent(void) const;
2039 const cEventInfo *GetFollowingEvent(void) const;
2040 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
2041 const cEventInfo *GetEventAround(time_t tTime) const;
2042 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
2045 const unsigned char GetTableID(void) const;
2046 const char *GetTimeString(void) const;
2047 const char *GetEndTimeString(void) const;
2048 const char *GetDate(void) const;
2049 bool IsFollowing(void) const;
2050 bool IsPresent(void) const;
2051 const char *GetExtendedDescription(void) const;
2052 const char *GetSubtitle(void) const;
2053 const char *GetTitle(void) const;
2054 unsigned short GetEventID(void) const;
2055 long GetDuration(void) const;
2056 time_t GetTime(void) const;
2057 tChannelID GetChannelID(void) const;
2058 int GetChannelNumber(void) const { return nChannelNumber; }
2059 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
2060 void Dump(FILE *f, const char *Prefix = "") const;
2064 void MVPClient::test(int channelNumber)
2066 FILE* f = fopen("/tmp/test.txt", "w");
2068 cMutexLock MutexLock;
2069 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
2073 fprintf(f, "Schedules = NULL\n");
2078 fprintf(f, "Schedules dump:\n");
2079 // Schedules->Dump(f);
2081 const cSchedule *Schedule;
2082 cChannel *thisChannel;
2083 const cEventInfo *event;
2085 thisChannel = channelFromNumber(channelNumber);
2088 fprintf(f, "thisChannel = NULL\n");
2093 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
2094 // Schedule = Schedules->GetSchedule();
2095 // Schedule = Schedules->First();
2098 fprintf(f, "First Schedule = NULL\n");
2103 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
2105 // For some channels VDR seems to pick a random point in time to
2106 // start dishing out events, but they are in order
2107 // at some point in the list the time snaps to the current event
2112 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
2114 event = Schedule->GetEventNumber(eventNumber);
2115 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
2116 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
2117 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
2121 fprintf(f, "\nEND\n");
2130 Schedules = the collection of all the Schedule objects
2131 Schedule = One schedule, contants all the events for a channel
2132 Event = One programme
2141 Subtitle (used for "Programmes resume at ...")
2144 IsPresent ? easy to work out tho. Oh it doesn't always work
2147 void MVPClient::test2()
2149 log->log("-", Log::DEBUG, "Timers List");
2151 for (int i = 0; i < Timers.Count(); i++)
2153 cTimer *timer = Timers.Get(i);
2154 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
2155 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
2156 #if VDRVERSNUM < 10300
2157 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());
2159 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());
2161 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
2162 log->log("-", Log::DEBUG, "");
2165 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
2166 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
2167 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
2171 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
2172 recording is a bool, 0 for not currently recording, 1 for currently recording
2173 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