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);
570 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
572 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
573 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
574 sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);
576 // FIXME Check whether this already exists before mkdiring it
578 log->log("Client", Log::DEBUG, "%s", newContainer);
582 int statret = stat(newContainer, &dstat);
583 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
585 log->log("Client", Log::DEBUG, "new dir does not exist");
586 int mkdirret = mkdir(newContainer, 0755);
589 delete[] dateDirName;
590 delete[] titleDirName;
591 delete[] newContainer;
595 tcp.sendPacket(rp->getPtr(), rp->getLen());
599 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
601 delete[] dateDirName;
602 delete[] titleDirName;
603 delete[] newContainer;
607 tcp.sendPacket(rp->getPtr(), rp->getLen());
611 // Ok, the directory container has been made, or it pre-existed.
613 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
614 sprintf(newDir, "%s/%s", newContainer, dateDirName);
616 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
617 int renameret = rename(t, newDir);
620 // Success. Test for remove old dir containter
621 char* oldTitleDir = new char[k+1];
622 memcpy(oldTitleDir, t, k);
623 oldTitleDir[k] = '\0';
624 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
625 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
626 delete[] oldTitleDir;
631 #if VDRVERSNUM > 10311
633 ::Recordings.Update();
635 // Success. Send a different packet from just a ulong
636 rp->addULONG(1); // success
637 rp->addString(newDir);
645 tcp.sendPacket(rp->getPtr(), rp->getLen());
647 delete[] dateDirName;
648 delete[] titleDirName;
649 delete[] newContainer;
656 tcp.sendPacket(rp->getPtr(), rp->getLen());
663 tcp.sendPacket(rp->getPtr(), rp->getLen());
669 int MVPClient::processGetChannelsList(UCHAR* data, int length, ResponsePacket* rp)
673 char* chanConfig = config.getValueString("General", "Channels");
675 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
677 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
679 #if VDRVERSNUM < 10300
680 if (!channel->GroupSep() && (!channel->Ca() || allChans))
682 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
685 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
687 if (channel->Vpid()) type = 1;
688 #if VDRVERSNUM < 10300
691 else if (channel->Apid(0)) type = 2;
695 rp->addULONG(channel->Number());
697 rp->addString(channel->Name());
702 tcp.sendPacket(rp->getPtr(), rp->getLen());
704 log->log("Client", Log::DEBUG, "Written channels list");
709 int MVPClient::processGetChannelPids(UCHAR* data, int length, ResponsePacket* rp)
711 ULONG channelNumber = ntohl(*(ULONG*)data);
713 cChannel* channel = channelFromNumber(channelNumber);
718 tcp.sendPacket(rp->getPtr(), rp->getLen());
724 #if VDRVERSNUM < 10300
726 log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
727 log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
729 if (channel->Apid2())
731 else if (channel->Apid1())
738 for (const int *Apid = channel->Apids(); *Apid; Apid++)
745 // Format of response
753 rp->addULONG(channel->Vpid());
754 rp->addULONG(numApids);
756 #if VDRVERSNUM < 10300
759 rp->addULONG(channel->Apid1());
764 rp->addULONG(channel->Apid2());
768 for (ULONG i = 0; i < numApids; i++)
770 rp->addULONG(channel->Apid(i));
771 rp->addString(channel->Alang(i));
776 tcp.sendPacket(rp->getPtr(), rp->getLen());
778 log->log("Client", Log::DEBUG, "Written channels pids");
783 int MVPClient::processStartStreamingChannel(UCHAR* data, int length, ULONG streamID, ResponsePacket* rp)
787 log->log("Client", Log::ERR, "Client called start streaming twice");
791 log->log("Client", Log::DEBUG, "length = %i", length);
792 ULONG channelNumber = ntohl(*(ULONG*)data);
794 cChannel* channel = channelFromNumber(channelNumber);
799 tcp.sendPacket(rp->getPtr(), rp->getLen());
803 // get the priority we should use
805 int priority = config.getValueLong("General", "Live priority", &fail);
808 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
812 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
817 if (priority < 0) priority = 0;
818 if (priority > 99) priority = 99;
820 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
821 lp = MVPReceiver::create(channel, priority);
827 tcp.sendPacket(rp->getPtr(), rp->getLen());
831 if (!lp->init(&tcp, streamID))
837 tcp.sendPacket(rp->getPtr(), rp->getLen());
843 tcp.sendPacket(rp->getPtr(), rp->getLen());
847 int MVPClient::processStopStreaming(UCHAR* data, int length, ResponsePacket* rp)
849 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
860 delete recordingManager;
862 recordingManager = NULL;
867 tcp.sendPacket(rp->getPtr(), rp->getLen());
871 int MVPClient::processGetBlock(UCHAR* data, int length, ResponsePacket* rp)
873 if (!lp && !recplayer)
875 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
879 ULLONG position = ntohll(*(ULLONG*)data);
880 data += sizeof(ULLONG);
881 ULONG amount = ntohl(*(ULONG*)data);
883 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
885 UCHAR sendBuffer[amount];
886 ULONG amountReceived = 0; // compiler moan.
889 log->log("Client", Log::DEBUG, "getting from live");
890 amountReceived = lp->getBlock(&sendBuffer[0], amount);
894 // vdr has possibly disconnected the receiver
895 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
902 log->log("Client", Log::DEBUG, "getting from recording");
903 amountReceived = recplayer->getBlock(&sendBuffer[0], position, amount);
909 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
913 rp->copyin(sendBuffer, amountReceived);
914 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
918 tcp.sendPacket(rp->getPtr(), rp->getLen());
919 log->log("Client", Log::DEBUG, "Finished getblock, have sent %lu", rp->getLen());
923 int MVPClient::processStartStreamingRecording(UCHAR* data, int length, ResponsePacket* rp)
925 // data is a pointer to the fileName string
927 recordingManager = new cRecordings;
928 recordingManager->Load();
930 cRecording* recording = recordingManager->GetByName((char*)data);
932 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
936 recplayer = new RecPlayer(recording);
938 rp->addULLONG(recplayer->getLengthBytes());
939 rp->addULONG(recplayer->getLengthFrames());
941 tcp.sendPacket(rp->getPtr(), rp->getLen());
943 log->log("Client", Log::DEBUG, "written totalLength");
947 delete recordingManager;
948 recordingManager = NULL;
953 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length, ResponsePacket* rp)
957 ULONG frameNumber = ntohl(*(ULONG*)data);
962 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
966 retval = recplayer->positionFromFrameNumber(frameNumber);
969 rp->addULLONG(retval);
971 tcp.sendPacket(rp->getPtr(), rp->getLen());
973 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
977 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length, ResponsePacket* rp)
981 ULLONG position = ntohll(*(ULLONG*)data);
986 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
990 retval = recplayer->frameNumberFromPosition(position);
993 rp->addULONG(retval);
995 tcp.sendPacket(rp->getPtr(), rp->getLen());
997 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1001 int MVPClient::processGetIFrame(UCHAR* data, int length, ResponsePacket* rp)
1003 bool success = false;
1005 ULONG frameNumber = ntohl(*(ULONG*)data);
1007 ULONG direction = ntohl(*(ULONG*)data);
1010 ULLONG rfilePosition = 0;
1011 ULONG rframeNumber = 0;
1012 ULONG rframeLength = 0;
1016 log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1020 success = recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1023 // returns file position, frame number, length
1027 rp->addULLONG(rfilePosition);
1028 rp->addULONG(rframeNumber);
1029 rp->addULONG(rframeLength);
1037 tcp.sendPacket(rp->getPtr(), rp->getLen());
1039 log->log("Client", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1043 int MVPClient::processGetChannelSchedule(UCHAR* data, int length, ResponsePacket* rp)
1045 ULONG channelNumber = ntohl(*(ULONG*)data);
1047 ULONG startTime = ntohl(*(ULONG*)data);
1049 ULONG duration = ntohl(*(ULONG*)data);
1051 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1053 cChannel* channel = channelFromNumber(channelNumber);
1058 tcp.sendPacket(rp->getPtr(), rp->getLen());
1060 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
1064 log->log("Client", Log::DEBUG, "Got channel");
1066 #if VDRVERSNUM < 10300
1067 cMutexLock MutexLock;
1068 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1070 cSchedulesLock MutexLock;
1071 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1077 tcp.sendPacket(rp->getPtr(), rp->getLen());
1079 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1083 log->log("Client", Log::DEBUG, "Got schedule!s! object");
1085 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1090 tcp.sendPacket(rp->getPtr(), rp->getLen());
1092 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
1096 log->log("Client", Log::DEBUG, "Got schedule object");
1098 const char* empty = "";
1099 bool atLeastOneEvent = false;
1102 ULONG thisEventTime;
1103 ULONG thisEventDuration;
1104 const char* thisEventTitle;
1105 const char* thisEventSubTitle;
1106 const char* thisEventDescription;
1108 #if VDRVERSNUM < 10300
1110 const cEventInfo *event;
1111 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1113 event = Schedule->GetEventNumber(eventNumber);
1115 thisEventID = event->GetEventID();
1116 thisEventTime = event->GetTime();
1117 thisEventDuration = event->GetDuration();
1118 thisEventTitle = event->GetTitle();
1119 thisEventSubTitle = event->GetSubtitle();
1120 thisEventDescription = event->GetExtendedDescription();
1124 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1126 thisEventID = event->EventID();
1127 thisEventTime = event->StartTime();
1128 thisEventDuration = event->Duration();
1129 thisEventTitle = event->Title();
1130 thisEventSubTitle = NULL;
1131 thisEventDescription = event->Description();
1135 log->log("Client", Log::DEBUG, "Got an event object %p", event);
1137 //in the past filter
1138 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1141 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1144 if (thisEventTime >= (startTime + duration)) continue;
1146 if (!thisEventTitle) thisEventTitle = empty;
1147 if (!thisEventSubTitle) thisEventSubTitle = empty;
1148 if (!thisEventDescription) thisEventDescription = empty;
1150 rp->addULONG(thisEventID);
1151 rp->addULONG(thisEventTime);
1152 rp->addULONG(thisEventDuration);
1154 rp->addString(thisEventTitle);
1155 rp->addString(thisEventSubTitle);
1156 rp->addString(thisEventDescription);
1158 atLeastOneEvent = true;
1159 log->log("Client", Log::DEBUG, "Done s3");
1162 log->log("Client", Log::DEBUG, "Got all event data");
1164 if (!atLeastOneEvent)
1167 log->log("Client", Log::DEBUG, "Written 0 because no data");
1171 tcp.sendPacket(rp->getPtr(), rp->getLen());
1173 log->log("Client", Log::DEBUG, "written schedules packet");
1178 #endif //VOMPSTANDALONE
1180 int MVPClient::processConfigSave(UCHAR* buffer, int length, ResponsePacket* rp)
1182 char* section = (char*)buffer;
1186 for (int k = 0; k < length; k++)
1188 if (buffer[k] == '\0')
1192 key = (char*)&buffer[k+1];
1196 value = (char*)&buffer[k+1];
1202 // if the last string (value) doesnt have null terminator, give up
1203 if (buffer[length - 1] != '\0') return 0;
1205 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1206 if (config.setValueString(section, key, value))
1216 tcp.sendPacket(rp->getPtr(), rp->getLen());
1221 int MVPClient::processConfigLoad(UCHAR* buffer, int length, ResponsePacket* rp)
1223 char* section = (char*)buffer;
1226 for (int k = 0; k < length; k++)
1228 if (buffer[k] == '\0')
1230 key = (char*)&buffer[k+1];
1235 char* value = config.getValueString(section, key);
1239 rp->addString(value);
1240 log->log("Client", Log::DEBUG, "Written config load packet");
1246 log->log("Client", Log::DEBUG, "Written config load failed packet");
1250 tcp.sendPacket(rp->getPtr(), rp->getLen());
1255 #ifndef VOMPSTANDALONE
1257 int MVPClient::processGetTimers(UCHAR* buffer, int length, ResponsePacket* rp)
1260 int numTimers = Timers.Count();
1262 rp->addULONG(numTimers);
1264 for (int i = 0; i < numTimers; i++)
1266 timer = Timers.Get(i);
1268 #if VDRVERSNUM < 10300
1269 rp->addULONG(timer->Active());
1271 rp->addULONG(timer->HasFlags(tfActive));
1273 rp->addULONG(timer->Recording());
1274 rp->addULONG(timer->Pending());
1275 rp->addULONG(timer->Priority());
1276 rp->addULONG(timer->Lifetime());
1277 rp->addULONG(timer->Channel()->Number());
1278 rp->addULONG(timer->StartTime());
1279 rp->addULONG(timer->StopTime());
1280 rp->addULONG(timer->Day());
1281 rp->addULONG(timer->WeekDays());
1282 rp->addString(timer->File());
1286 tcp.sendPacket(rp->getPtr(), rp->getLen());
1288 log->log("Client", Log::DEBUG, "Written timers list");
1293 int MVPClient::processSetTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1295 char* timerString = new char[strlen((char*)buffer) + 1];
1296 strcpy(timerString, (char*)buffer);
1298 #if VDRVERSNUM < 10300
1300 // If this is VDR 1.2 the date part of the timer string must be reduced
1301 // to just DD rather than YYYY-MM-DD
1303 int s = 0; // source
1304 int d = 0; // destination
1306 while(c != 2) // copy up to date section, including the second ':'
1308 timerString[d] = buffer[s];
1309 if (buffer[s] == ':') c++;
1313 // now it has copied up to the date section
1315 while(c != 2) // waste YYYY-MM-
1317 if (buffer[s] == '-') c++;
1320 // now source is at the DD
1321 memcpy(&timerString[d], &buffer[s], length - s);
1323 timerString[d] = '\0';
1325 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1326 log->log("Client", Log::DEBUG, "%s", timerString);
1330 cTimer *timer = new cTimer;
1331 if (timer->Parse((char*)timerString))
1333 cTimer *t = Timers.GetTimer(timer);
1337 #if VDRVERSNUM < 10300
1340 Timers.SetModified();
1344 tcp.sendPacket(rp->getPtr(), rp->getLen());
1351 tcp.sendPacket(rp->getPtr(), rp->getLen());
1358 tcp.sendPacket(rp->getPtr(), rp->getLen());
1364 int MVPClient::processDeleteTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1366 log->log("Client", Log::DEBUG, "Delete timer called");
1371 INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4;
1372 INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4;
1373 INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4;
1374 INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4;
1375 INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4;
1378 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1380 if ( (ti->Channel()->Number() == delChannel)
1381 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1382 && (ti->StartTime() == delStart)
1383 && (ti->StopTime() == delStop) )
1391 tcp.sendPacket(rp->getPtr(), rp->getLen());
1395 if (!Timers.BeingEdited())
1397 if (!ti->Recording())
1400 Timers.SetModified();
1403 tcp.sendPacket(rp->getPtr(), rp->getLen());
1408 log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
1411 tcp.sendPacket(rp->getPtr(), rp->getLen());
1417 log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1420 tcp.sendPacket(rp->getPtr(), rp->getLen());
1425 int MVPClient::processGetRecInfo(UCHAR* data, int length, ResponsePacket* rp)
1427 // data is a pointer to the fileName string
1429 cRecordings Recordings;
1430 Recordings.Load(); // probably have to do this
1432 cRecording *recording = Recordings.GetByName((char*)data);
1434 time_t timerStart = 0;
1435 time_t timerStop = 0;
1436 char* summary = NULL;
1437 ULONG resumePoint = 0;
1441 log->log("Client", Log::ERR, "GetRecInfo found no recording");
1444 tcp.sendPacket(rp->getPtr(), rp->getLen());
1449 4 bytes: start time for timer
1450 4 bytes: end time for timer
1451 4 bytes: resume point
1453 4 bytes: num components
1463 // Get current timer
1465 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1468 timerStart = rc->Timer()->StartTime();
1469 timerStop = rc->Timer()->StopTime();
1470 log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1473 rp->addULONG(timerStart);
1474 rp->addULONG(timerStop);
1478 char* value = config.getValueString("ResumeData", (char*)data);
1481 resumePoint = strtoul(value, NULL, 10);
1484 log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1486 rp->addULONG(resumePoint);
1491 #if VDRVERSNUM < 10300
1492 summary = (char*)recording->Summary();
1494 const cRecordingInfo *Info = recording->Info();
1495 summary = (char*)Info->ShortText();
1496 if (isempty(summary)) summary = (char*)Info->Description();
1498 log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1501 rp->addString(summary);
1510 #if VDRVERSNUM < 10300
1512 // Send 0 for numchannels - this signals the client this info is not available
1516 const cComponents* components = Info->Components();
1518 log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1526 rp->addULONG(components->NumComponents());
1528 tComponent* component;
1529 for (int i = 0; i < components->NumComponents(); i++)
1531 component = components->Component(i);
1533 log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1535 rp->addUCHAR(component->stream);
1536 rp->addUCHAR(component->type);
1538 if (component->language)
1540 rp->addString(component->language);
1546 if (component->description)
1548 rp->addString(component->description);
1562 tcp.sendPacket(rp->getPtr(), rp->getLen());
1564 log->log("Client", Log::DEBUG, "Written getrecinfo");
1574 int MVPClient::processReScanRecording(UCHAR* data, int length, ResponsePacket* rp)
1578 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1584 rp->addULLONG(recplayer->getLengthBytes());
1585 rp->addULONG(recplayer->getLengthFrames());
1587 tcp.sendPacket(rp->getPtr(), rp->getLen());
1588 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1592 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1595 int MVPClient::processGetMarks(UCHAR* data, int length, ResponsePacket* rp)
1597 // data is a pointer to the fileName string
1600 cRecordings Recordings;
1601 Recordings.Load(); // probably have to do this
1603 cRecording *recording = Recordings.GetByName((char*)data);
1605 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1609 Marks.Load(recording->FileName());
1612 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1614 log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1616 rp->addULONG(m->position);
1621 log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1627 tcp.sendPacket(rp->getPtr(), rp->getLen());
1629 log->log("Client", Log::DEBUG, "Written Marks list");
1634 #endif //VOMPSTANDALONE
1637 * media List Request:
1639 * 4 VDR_GETMEDIALIST
1640 * 4 flags (currently unused)
1643 * Media List response:
1651 * 4 strlen (incl. 0 Byte)
1656 int MVPClient::processGetMediaList(UCHAR* data, int length, ResponsePacket* rp)
1659 log->log("Client", Log::ERR, "getMediaList packet too short %d", length);
1662 char * dirname=NULL;
1664 //we have a dirname provided
1665 dirname=(char *)&data[4];
1666 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
1669 MediaList * ml=MediaList::readList(baseConfig,dirname);
1671 log->log("Client", Log::ERR, "getMediaList returned NULL");
1675 //response code (not yet set)
1679 rp->addULONG(ml->size());
1681 for (MediaList::iterator nm=ml->begin(); nm<ml->end(); nm++)
1684 log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
1685 rp->addULONG(m->getType());
1687 rp->addULONG(m->getTime());
1690 int len=strlen(m->getFilename());
1692 rp->addULONG(len+1);
1693 rp->addString(m->getFilename());
1698 tcp.sendPacket(rp->getPtr(), rp->getLen());
1700 log->log("Client", Log::DEBUG, "Written Media list");
1705 * get image Request:
1706 * 4 flags (currently unused)
1711 * get image response:
1717 int MVPClient::processGetPicture(UCHAR* data, int length, ResponsePacket* rp)
1720 log->log("Client", Log::ERR, "getPicture packet too short %d", length);
1727 char * filename=NULL;
1729 //we have a dirname provided
1730 filename=(char *)&data[12];
1731 log->log("Client", Log::DEBUG, "getPicture %s", filename);
1734 log->log("Client", Log::ERR, "getPicture empty filename");
1737 imageFile=fopen(filename,"r");
1738 if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename);
1743 if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size;
1745 //response code (not yet set)
1750 log->log("Client", Log::DEBUG, "getPicture size %u", size);
1753 tcp.sendPacket(rp->getPtr(), rp->getLen());
1755 log->log("Client", Log::DEBUG, "Written getPicture");
1761 int MVPClient::processGetImageBlock(UCHAR* data, int length, ResponsePacket* rp)
1765 log->log("Client", Log::DEBUG, "Get image block called when no image active");
1769 ULLONG position = ntohll(*(ULLONG*)data);
1770 data += sizeof(ULLONG);
1771 ULONG amount = ntohl(*(ULONG*)data);
1773 log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
1775 UCHAR sendBuffer[amount];
1776 ULONG amountReceived = 0; // compiler moan.
1777 ULLONG cpos=ftell(imageFile);
1778 if (position != cpos) {
1779 fseek(imageFile,position-cpos,SEEK_CUR);
1781 if (position != (ULLONG)ftell(imageFile)) {
1782 log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position);
1785 amountReceived=fread(&sendBuffer[0],1,amount,imageFile);
1788 if (!amountReceived)
1791 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
1795 rp->copyin(sendBuffer, amount);
1796 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
1800 tcp.sendPacket(rp->getPtr(), rp->getLen());
1806 int MVPClient::processGetLanguageList(UCHAR* data, int length, ResponsePacket* rp)
1808 i18n.findLanguages();
1809 const I18n::lang_code_list& languages = i18n.getLanguageList();
1811 I18n::lang_code_list::const_iterator iter;
1812 for (iter = languages.begin(); iter != languages.end(); ++iter)
1814 rp->addString(iter->first.c_str());
1815 rp->addString(iter->second.c_str());
1818 tcp.sendPacket(rp->getPtr(), rp->getLen());
1822 int MVPClient::processGetLanguageContent(UCHAR* data, int length, ResponsePacket* rp)
1824 if (length <= 0) return 0;
1825 std::string code, result;
1826 code.assign((char*)data, length - 1);
1827 i18n.findLanguages();
1828 I18n::trans_table texts = i18n.getLanguageContent(code);
1829 I18n::trans_table::const_iterator iter;
1830 for (iter = texts.begin(); iter != texts.end(); ++iter)
1832 rp->addString(iter->first.c_str());
1833 rp->addString(iter->second.c_str());
1836 tcp.sendPacket(rp->getPtr(), rp->getLen());
1854 event = Schedule->GetPresentEvent();
1856 fprintf(f, "\n\nCurrent event\n\n");
1858 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1859 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1860 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1861 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1863 event = Schedule->GetFollowingEvent();
1865 fprintf(f, "\n\nFollowing event\n\n");
1867 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1868 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1869 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1870 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1874 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1875 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1876 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1877 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1883 void MVPClient::test2()
1885 FILE* f = fopen("/tmp/s.txt", "w");
1887 #if VDRVERSNUM < 10300
1888 cMutexLock MutexLock;
1889 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1891 cSchedulesLock MutexLock;
1892 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1897 fprintf(f, "Schedules = NULL\n");
1902 fprintf(f, "Schedules dump:\n");
1906 const cSchedule *Schedule;
1907 int scheduleNumber = 0;
1910 cChannel *thisChannel;
1912 #if VDRVERSNUM < 10300
1913 const cEventInfo *event;
1914 int eventNumber = 0;
1916 const cEvent *event;
1919 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1920 // Schedule = Schedules->GetSchedule();
1921 Schedule = Schedules->First();
1924 fprintf(f, "First Schedule = NULL\n");
1931 fprintf(f, "Schedule #%i\n", scheduleNumber);
1932 fprintf(f, "-------------\n\n");
1934 #if VDRVERSNUM < 10300
1935 tchid = Schedule->GetChannelID();
1937 tchid = Schedule->ChannelID();
1940 #if VDRVERSNUM < 10300
1941 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1942 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1944 // put the count at the end.
1947 thisChannel = Channels.GetByChannelID(tchid, true);
1950 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1954 fprintf(f, "thisChannel = NULL for tchid\n");
1957 #if VDRVERSNUM < 10300
1958 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1960 event = Schedule->GetEventNumber(eventNumber);
1961 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1962 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1963 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1964 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1965 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1966 fprintf(f, "Event %u dump:\n", eventNumber);
1971 // This whole section needs rewriting to walk the list.
1972 event = Schedule->Events()->First();
1974 event = Schedule->Events()->Next(event);
1979 fprintf(f, "\nDump from object:\n");
1981 fprintf(f, "\nEND\n");
1986 fprintf(f, "End of current Schedule\n\n\n");
1988 Schedule = (const cSchedule *)Schedules->Next(Schedule);
1996 const cEventInfo *GetPresentEvent(void) const;
1997 const cEventInfo *GetFollowingEvent(void) const;
1998 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
1999 const cEventInfo *GetEventAround(time_t tTime) const;
2000 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
2003 const unsigned char GetTableID(void) const;
2004 const char *GetTimeString(void) const;
2005 const char *GetEndTimeString(void) const;
2006 const char *GetDate(void) const;
2007 bool IsFollowing(void) const;
2008 bool IsPresent(void) const;
2009 const char *GetExtendedDescription(void) const;
2010 const char *GetSubtitle(void) const;
2011 const char *GetTitle(void) const;
2012 unsigned short GetEventID(void) const;
2013 long GetDuration(void) const;
2014 time_t GetTime(void) const;
2015 tChannelID GetChannelID(void) const;
2016 int GetChannelNumber(void) const { return nChannelNumber; }
2017 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
2018 void Dump(FILE *f, const char *Prefix = "") const;
2022 void MVPClient::test(int channelNumber)
2024 FILE* f = fopen("/tmp/test.txt", "w");
2026 cMutexLock MutexLock;
2027 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
2031 fprintf(f, "Schedules = NULL\n");
2036 fprintf(f, "Schedules dump:\n");
2037 // Schedules->Dump(f);
2039 const cSchedule *Schedule;
2040 cChannel *thisChannel;
2041 const cEventInfo *event;
2043 thisChannel = channelFromNumber(channelNumber);
2046 fprintf(f, "thisChannel = NULL\n");
2051 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
2052 // Schedule = Schedules->GetSchedule();
2053 // Schedule = Schedules->First();
2056 fprintf(f, "First Schedule = NULL\n");
2061 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
2063 // For some channels VDR seems to pick a random point in time to
2064 // start dishing out events, but they are in order
2065 // at some point in the list the time snaps to the current event
2070 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
2072 event = Schedule->GetEventNumber(eventNumber);
2073 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
2074 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
2075 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
2079 fprintf(f, "\nEND\n");
2088 Schedules = the collection of all the Schedule objects
2089 Schedule = One schedule, contants all the events for a channel
2090 Event = One programme
2099 Subtitle (used for "Programmes resume at ...")
2102 IsPresent ? easy to work out tho. Oh it doesn't always work
2105 void MVPClient::test2()
2107 log->log("-", Log::DEBUG, "Timers List");
2109 for (int i = 0; i < Timers.Count(); i++)
2111 cTimer *timer = Timers.Get(i);
2112 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
2113 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
2114 #if VDRVERSNUM < 10300
2115 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());
2117 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());
2119 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
2120 log->log("-", Log::DEBUG, "");
2123 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
2124 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
2125 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
2129 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
2130 recording is a bool, 0 for not currently recording, 1 for currently recording
2131 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