2 Copyright 2004-2008 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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 if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
248 requestID = ntohl(requestID);
250 if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
251 opcode = ntohl(opcode);
253 if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
254 extraDataLength = ntohl(extraDataLength);
255 if (extraDataLength > 200000) // a random sanity limit
257 log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
263 data = (UCHAR*)malloc(extraDataLength);
266 log->log("Client", Log::ERR, "Extra data buffer malloc error");
270 if (!tcp.readData(data, extraDataLength))
272 log->log("Client", Log::ERR, "Could not read extradata");
282 log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
284 if (!loggedIn && (opcode != 1))
286 log->log("Client", Log::ERR, "Not logged in and opcode != 1");
287 if (data) free(data);
291 ResponsePacket* rp = new ResponsePacket();
292 if (!rp->init(requestID))
294 log->log("Client", Log::ERR, "response packet init fail");
302 result = processLogin(data, extraDataLength, rp);
304 #ifndef VOMPSTANDALONE
306 result = processGetRecordingsList(data, extraDataLength, rp);
309 result = processDeleteRecording(data, extraDataLength, rp);
312 result = processGetChannelsList(data, extraDataLength, rp);
315 result = processStartStreamingChannel(data, extraDataLength, requestID, rp);
318 result = processGetBlock(data, extraDataLength, rp);
321 result = processStopStreaming(data, extraDataLength, rp);
324 result = processStartStreamingRecording(data, extraDataLength, rp);
328 result = processGetChannelSchedule(data, extraDataLength, rp);
331 result = processConfigSave(data, extraDataLength, rp);
334 result = processConfigLoad(data, extraDataLength, rp);
336 #ifndef VOMPSTANDALONE
338 result = processReScanRecording(data, extraDataLength, rp); // FIXME obselete
341 result = processGetTimers(data, extraDataLength, rp);
344 result = processSetTimer(data, extraDataLength, rp);
347 result = processPositionFromFrameNumber(data, extraDataLength, rp);
350 result = processFrameNumberFromPosition(data, extraDataLength, rp);
353 result = processMoveRecording(data, extraDataLength, rp);
356 result = processGetIFrame(data, extraDataLength, rp);
359 result = processGetRecInfo(data, extraDataLength, rp);
362 result = processGetMarks(data, extraDataLength, rp);
365 result = processGetChannelPids(data, extraDataLength, rp);
368 result = processDeleteTimer(data, extraDataLength, rp);
372 result = processGetMediaList(data, extraDataLength, rp);
375 result = processGetPicture(data, extraDataLength, rp);
378 result = processGetImageBlock(data, extraDataLength, rp);
381 result = processGetLanguageList(data, extraDataLength, rp);
384 result = processGetLanguageContent(data, extraDataLength, rp);
389 if (data) free(data);
394 int MVPClient::processLogin(UCHAR* buffer, int length, ResponsePacket* rp)
396 if (length != 6) return 0;
400 char configFileName[PATH_MAX];
401 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]);
402 config.init(configFileName);
404 // Send the login reply
406 time_t timeNow = time(NULL);
407 struct tm* timeStruct = localtime(&timeNow);
408 int timeOffset = timeStruct->tm_gmtoff;
410 rp->addULONG(timeNow);
411 rp->addLONG(timeOffset);
413 tcp.sendPacket(rp->getPtr(), rp->getLen());
414 log->log("Client", Log::DEBUG, "written login reply len %lu", rp->getLen());
420 #ifndef VOMPSTANDALONE
421 int MVPClient::processGetRecordingsList(UCHAR* data, int length, ResponsePacket* rp)
424 int Percent = VideoDiskSpace(&FreeMB);
425 int Total = (FreeMB / (100 - Percent)) * 100;
428 rp->addULONG(FreeMB);
429 rp->addULONG(Percent);
431 cRecordings Recordings;
434 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
436 rp->addULONG(recording->start);
437 rp->addString(recording->Name());
438 rp->addString(recording->FileName());
442 tcp.sendPacket(rp->getPtr(), rp->getLen());
444 log->log("Client", Log::DEBUG, "Written recordings list");
449 int MVPClient::processDeleteRecording(UCHAR* data, int length, ResponsePacket* rp)
451 // data is a pointer to the fileName string
453 cRecordings Recordings;
454 Recordings.Load(); // probably have to do this
456 cRecording* recording = Recordings.GetByName((char*)data);
458 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
462 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
464 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
467 if (recording->Delete())
469 // Copy svdrp's way of doing this, see if it works
470 #if VDRVERSNUM > 10300
471 ::Recordings.DelByName(recording->FileName());
491 tcp.sendPacket(rp->getPtr(), rp->getLen());
496 int MVPClient::processMoveRecording(UCHAR* data, int length, ResponsePacket* rp)
498 log->log("Client", Log::DEBUG, "Process move recording");
499 char* fileName = (char*)data;
500 char* newPath = NULL;
502 for (int k = 0; k < length; k++)
506 newPath = (char*)&data[k+1];
510 if (!newPath) return 0;
512 cRecordings Recordings;
513 Recordings.Load(); // probably have to do this
515 cRecording* recording = Recordings.GetByName((char*)fileName);
517 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
521 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
524 log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
525 log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
526 log->log("Client", Log::DEBUG, "to: %s", newPath);
528 const char* t = recording->FileName();
530 char* dateDirName = NULL; int k;
531 char* titleDirName = NULL; int j;
533 // Find the datedirname
534 for(k = strlen(t) - 1; k >= 0; k--)
538 log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
539 dateDirName = new char[strlen(&t[k+1]) + 1];
540 strcpy(dateDirName, &t[k+1]);
545 // Find the titledirname
547 for(j = k-1; j >= 0; j--)
551 log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
552 titleDirName = new char[(k - j - 1) + 1];
553 memcpy(titleDirName, &t[j+1], k - j - 1);
554 titleDirName[k - j - 1] = '\0';
559 log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
560 log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
561 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
563 char* newPathConv = new char[strlen(newPath)+1];
564 strcpy(newPathConv, newPath);
565 ExchangeChars(newPathConv, true);
566 log->log("Client", Log::DEBUG, "EC: %s", newPathConv);
568 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
569 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
570 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
571 delete[] newPathConv;
573 log->log("Client", Log::DEBUG, "%s", newContainer);
576 int statret = stat(newContainer, &dstat);
577 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
579 log->log("Client", Log::DEBUG, "new dir does not exist");
580 int mkdirret = mkdir(newContainer, 0755);
583 delete[] dateDirName;
584 delete[] titleDirName;
585 delete[] newContainer;
589 tcp.sendPacket(rp->getPtr(), rp->getLen());
593 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
595 delete[] dateDirName;
596 delete[] titleDirName;
597 delete[] newContainer;
601 tcp.sendPacket(rp->getPtr(), rp->getLen());
605 // Ok, the directory container has been made, or it pre-existed.
607 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
608 sprintf(newDir, "%s/%s", newContainer, dateDirName);
610 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
611 int renameret = rename(t, newDir);
614 // Success. Test for remove old dir containter
615 char* oldTitleDir = new char[k+1];
616 memcpy(oldTitleDir, t, k);
617 oldTitleDir[k] = '\0';
618 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
619 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
620 delete[] oldTitleDir;
625 #if VDRVERSNUM > 10311
627 ::Recordings.Update();
629 // Success. Send a different packet from just a ulong
630 rp->addULONG(1); // success
631 rp->addString(newDir);
639 tcp.sendPacket(rp->getPtr(), rp->getLen());
641 delete[] dateDirName;
642 delete[] titleDirName;
643 delete[] newContainer;
650 tcp.sendPacket(rp->getPtr(), rp->getLen());
657 tcp.sendPacket(rp->getPtr(), rp->getLen());
663 int MVPClient::processGetChannelsList(UCHAR* data, int length, ResponsePacket* rp)
667 char* chanConfig = config.getValueString("General", "Channels");
669 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
671 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
673 #if VDRVERSNUM < 10300
674 if (!channel->GroupSep() && (!channel->Ca() || allChans))
676 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
679 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
681 if (channel->Vpid()) type = 1;
682 #if VDRVERSNUM < 10300
685 else if (channel->Apid(0)) type = 2;
689 rp->addULONG(channel->Number());
691 rp->addString(channel->Name());
696 tcp.sendPacket(rp->getPtr(), rp->getLen());
698 log->log("Client", Log::DEBUG, "Written channels list");
703 int MVPClient::processGetChannelPids(UCHAR* data, int length, ResponsePacket* rp)
705 ULONG channelNumber = ntohl(*(ULONG*)data);
707 cChannel* channel = channelFromNumber(channelNumber);
712 tcp.sendPacket(rp->getPtr(), rp->getLen());
721 #if VDRVERSNUM < 10300
723 log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
724 log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
726 if (channel->Apid2())
728 else if (channel->Apid1())
735 for (const int *Apid = channel->Apids(); *Apid; Apid++)
739 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
743 for (const int *Spid = channel->Spids(); *Spid; Spid++)
750 // Format of response
769 rp->addULONG(channel->Vpid());
770 rp->addULONG(numApids);
772 #if VDRVERSNUM < 10300
775 rp->addULONG(channel->Apid1());
780 rp->addULONG(channel->Apid2());
786 for (ULONG i = 0; i < numApids; i++)
788 rp->addULONG(channel->Apid(i));
789 rp->addString(channel->Alang(i));
791 rp->addULONG(numDpids);
792 for (ULONG i = 0; i < numDpids; i++)
794 rp->addULONG(channel->Dpid(i));
795 rp->addString(channel->Dlang(i));
797 rp->addULONG(numSpids);
798 for (ULONG i = 0; i < numSpids; i++)
800 rp->addULONG(channel->Spid(i));
801 rp->addString(channel->Slang(i));
804 rp->addULONG(channel->Tpid());
808 tcp.sendPacket(rp->getPtr(), rp->getLen());
810 log->log("Client", Log::DEBUG, "Written channels pids");
815 int MVPClient::processStartStreamingChannel(UCHAR* data, int length, ULONG streamID, ResponsePacket* rp)
819 log->log("Client", Log::ERR, "Client called start streaming twice");
823 log->log("Client", Log::DEBUG, "length = %i", length);
824 ULONG channelNumber = ntohl(*(ULONG*)data);
826 cChannel* channel = channelFromNumber(channelNumber);
831 tcp.sendPacket(rp->getPtr(), rp->getLen());
835 // get the priority we should use
837 int priority = config.getValueLong("General", "Live priority", &fail);
840 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
844 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
849 if (priority < 0) priority = 0;
850 if (priority > 99) priority = 99;
852 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
853 lp = MVPReceiver::create(channel, priority);
859 tcp.sendPacket(rp->getPtr(), rp->getLen());
863 if (!lp->init(&tcp, streamID))
869 tcp.sendPacket(rp->getPtr(), rp->getLen());
875 tcp.sendPacket(rp->getPtr(), rp->getLen());
879 int MVPClient::processStopStreaming(UCHAR* data, int length, ResponsePacket* rp)
881 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
892 delete recordingManager;
894 recordingManager = NULL;
899 tcp.sendPacket(rp->getPtr(), rp->getLen());
903 int MVPClient::processGetBlock(UCHAR* data, int length, ResponsePacket* rp)
905 if (!lp && !recplayer)
907 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
911 ULLONG position = ntohll(*(ULLONG*)data);
912 data += sizeof(ULLONG);
913 ULONG amount = ntohl(*(ULONG*)data);
915 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
917 UCHAR sendBuffer[amount];
918 ULONG amountReceived = 0; // compiler moan.
921 log->log("Client", Log::DEBUG, "getting from live");
922 amountReceived = lp->getBlock(&sendBuffer[0], amount);
926 // vdr has possibly disconnected the receiver
927 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
934 log->log("Client", Log::DEBUG, "getting from recording");
935 amountReceived = recplayer->getBlock(&sendBuffer[0], position, amount);
941 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
945 rp->copyin(sendBuffer, amountReceived);
946 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
950 tcp.sendPacket(rp->getPtr(), rp->getLen());
951 log->log("Client", Log::DEBUG, "Finished getblock, have sent %lu", rp->getLen());
955 int MVPClient::processStartStreamingRecording(UCHAR* data, int length, ResponsePacket* rp)
957 // data is a pointer to the fileName string
959 recordingManager = new cRecordings;
960 recordingManager->Load();
962 cRecording* recording = recordingManager->GetByName((char*)data);
964 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
968 recplayer = new RecPlayer(recording);
970 rp->addULLONG(recplayer->getLengthBytes());
971 rp->addULONG(recplayer->getLengthFrames());
973 tcp.sendPacket(rp->getPtr(), rp->getLen());
975 log->log("Client", Log::DEBUG, "written totalLength");
979 delete recordingManager;
980 recordingManager = NULL;
985 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length, ResponsePacket* rp)
989 ULONG frameNumber = ntohl(*(ULONG*)data);
994 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
998 retval = recplayer->positionFromFrameNumber(frameNumber);
1001 rp->addULLONG(retval);
1003 tcp.sendPacket(rp->getPtr(), rp->getLen());
1005 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1009 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length, ResponsePacket* rp)
1013 ULLONG position = ntohll(*(ULLONG*)data);
1018 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1022 retval = recplayer->frameNumberFromPosition(position);
1025 rp->addULONG(retval);
1027 tcp.sendPacket(rp->getPtr(), rp->getLen());
1029 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1033 int MVPClient::processGetIFrame(UCHAR* data, int length, ResponsePacket* rp)
1035 bool success = false;
1037 ULONG frameNumber = ntohl(*(ULONG*)data);
1039 ULONG direction = ntohl(*(ULONG*)data);
1042 ULLONG rfilePosition = 0;
1043 ULONG rframeNumber = 0;
1044 ULONG rframeLength = 0;
1048 log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1052 success = recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1055 // returns file position, frame number, length
1059 rp->addULLONG(rfilePosition);
1060 rp->addULONG(rframeNumber);
1061 rp->addULONG(rframeLength);
1069 tcp.sendPacket(rp->getPtr(), rp->getLen());
1071 log->log("Client", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1075 int MVPClient::processGetChannelSchedule(UCHAR* data, int length, ResponsePacket* rp)
1077 ULONG channelNumber = ntohl(*(ULONG*)data);
1079 ULONG startTime = ntohl(*(ULONG*)data);
1081 ULONG duration = ntohl(*(ULONG*)data);
1083 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1085 cChannel* channel = channelFromNumber(channelNumber);
1090 tcp.sendPacket(rp->getPtr(), rp->getLen());
1092 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
1096 log->log("Client", Log::DEBUG, "Got channel");
1098 #if VDRVERSNUM < 10300
1099 cMutexLock MutexLock;
1100 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1102 cSchedulesLock MutexLock;
1103 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1109 tcp.sendPacket(rp->getPtr(), rp->getLen());
1111 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1115 log->log("Client", Log::DEBUG, "Got schedule!s! object");
1117 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1122 tcp.sendPacket(rp->getPtr(), rp->getLen());
1124 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
1128 log->log("Client", Log::DEBUG, "Got schedule object");
1130 const char* empty = "";
1131 bool atLeastOneEvent = false;
1134 ULONG thisEventTime;
1135 ULONG thisEventDuration;
1136 const char* thisEventTitle;
1137 const char* thisEventSubTitle;
1138 const char* thisEventDescription;
1140 #if VDRVERSNUM < 10300
1142 const cEventInfo *event;
1143 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1145 event = Schedule->GetEventNumber(eventNumber);
1147 thisEventID = event->GetEventID();
1148 thisEventTime = event->GetTime();
1149 thisEventDuration = event->GetDuration();
1150 thisEventTitle = event->GetTitle();
1151 thisEventSubTitle = event->GetSubtitle();
1152 thisEventDescription = event->GetExtendedDescription();
1156 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1158 thisEventID = event->EventID();
1159 thisEventTime = event->StartTime();
1160 thisEventDuration = event->Duration();
1161 thisEventTitle = event->Title();
1162 thisEventSubTitle = NULL;
1163 thisEventDescription = event->Description();
1167 //in the past filter
1168 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1171 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1174 if (thisEventTime >= (startTime + duration)) continue;
1176 if (!thisEventTitle) thisEventTitle = empty;
1177 if (!thisEventSubTitle) thisEventSubTitle = empty;
1178 if (!thisEventDescription) thisEventDescription = empty;
1180 rp->addULONG(thisEventID);
1181 rp->addULONG(thisEventTime);
1182 rp->addULONG(thisEventDuration);
1184 rp->addString(thisEventTitle);
1185 rp->addString(thisEventSubTitle);
1186 rp->addString(thisEventDescription);
1188 atLeastOneEvent = true;
1191 log->log("Client", Log::DEBUG, "Got all event data");
1193 if (!atLeastOneEvent)
1196 log->log("Client", Log::DEBUG, "Written 0 because no data");
1200 tcp.sendPacket(rp->getPtr(), rp->getLen());
1202 log->log("Client", Log::DEBUG, "written schedules packet");
1207 #endif //VOMPSTANDALONE
1209 int MVPClient::processConfigSave(UCHAR* buffer, int length, ResponsePacket* rp)
1211 char* section = (char*)buffer;
1215 for (int k = 0; k < length; k++)
1217 if (buffer[k] == '\0')
1221 key = (char*)&buffer[k+1];
1225 value = (char*)&buffer[k+1];
1231 // if the last string (value) doesnt have null terminator, give up
1232 if (buffer[length - 1] != '\0') return 0;
1234 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1235 if (config.setValueString(section, key, value))
1245 tcp.sendPacket(rp->getPtr(), rp->getLen());
1250 int MVPClient::processConfigLoad(UCHAR* buffer, int length, ResponsePacket* rp)
1252 char* section = (char*)buffer;
1255 for (int k = 0; k < length; k++)
1257 if (buffer[k] == '\0')
1259 key = (char*)&buffer[k+1];
1264 char* value = config.getValueString(section, key);
1268 rp->addString(value);
1269 log->log("Client", Log::DEBUG, "Written config load packet");
1275 log->log("Client", Log::DEBUG, "Written config load failed packet");
1279 tcp.sendPacket(rp->getPtr(), rp->getLen());
1284 #ifndef VOMPSTANDALONE
1286 int MVPClient::processGetTimers(UCHAR* buffer, int length, ResponsePacket* rp)
1289 int numTimers = Timers.Count();
1291 rp->addULONG(numTimers);
1293 for (int i = 0; i < numTimers; i++)
1295 timer = Timers.Get(i);
1297 #if VDRVERSNUM < 10300
1298 rp->addULONG(timer->Active());
1300 rp->addULONG(timer->HasFlags(tfActive));
1302 rp->addULONG(timer->Recording());
1303 rp->addULONG(timer->Pending());
1304 rp->addULONG(timer->Priority());
1305 rp->addULONG(timer->Lifetime());
1306 rp->addULONG(timer->Channel()->Number());
1307 rp->addULONG(timer->StartTime());
1308 rp->addULONG(timer->StopTime());
1309 rp->addULONG(timer->Day());
1310 rp->addULONG(timer->WeekDays());
1311 rp->addString(timer->File());
1315 tcp.sendPacket(rp->getPtr(), rp->getLen());
1317 log->log("Client", Log::DEBUG, "Written timers list");
1322 int MVPClient::processSetTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1324 char* timerString = new char[strlen((char*)buffer) + 1];
1325 strcpy(timerString, (char*)buffer);
1327 #if VDRVERSNUM < 10300
1329 // If this is VDR 1.2 the date part of the timer string must be reduced
1330 // to just DD rather than YYYY-MM-DD
1332 int s = 0; // source
1333 int d = 0; // destination
1335 while(c != 2) // copy up to date section, including the second ':'
1337 timerString[d] = buffer[s];
1338 if (buffer[s] == ':') c++;
1342 // now it has copied up to the date section
1344 while(c != 2) // waste YYYY-MM-
1346 if (buffer[s] == '-') c++;
1349 // now source is at the DD
1350 memcpy(&timerString[d], &buffer[s], length - s);
1352 timerString[d] = '\0';
1354 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1355 log->log("Client", Log::DEBUG, "%s", timerString);
1359 cTimer *timer = new cTimer;
1360 if (timer->Parse((char*)timerString))
1362 cTimer *t = Timers.GetTimer(timer);
1366 #if VDRVERSNUM < 10300
1369 Timers.SetModified();
1373 tcp.sendPacket(rp->getPtr(), rp->getLen());
1380 tcp.sendPacket(rp->getPtr(), rp->getLen());
1387 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());
1424 if (!Timers.BeingEdited())
1426 if (!ti->Recording())
1429 Timers.SetModified();
1432 tcp.sendPacket(rp->getPtr(), rp->getLen());
1437 log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
1440 tcp.sendPacket(rp->getPtr(), rp->getLen());
1446 log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1449 tcp.sendPacket(rp->getPtr(), rp->getLen());
1454 int MVPClient::processGetRecInfo(UCHAR* data, int length, ResponsePacket* rp)
1456 // data is a pointer to the fileName string
1458 cRecordings Recordings;
1459 Recordings.Load(); // probably have to do this
1461 cRecording *recording = Recordings.GetByName((char*)data);
1463 time_t timerStart = 0;
1464 time_t timerStop = 0;
1465 char* summary = NULL;
1466 ULONG resumePoint = 0;
1470 log->log("Client", Log::ERR, "GetRecInfo found no recording");
1473 tcp.sendPacket(rp->getPtr(), rp->getLen());
1478 4 bytes: start time for timer
1479 4 bytes: end time for timer
1480 4 bytes: resume point
1482 4 bytes: num components
1492 // Get current timer
1494 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1497 timerStart = rc->Timer()->StartTime();
1498 timerStop = rc->Timer()->StopTime();
1499 log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1502 rp->addULONG(timerStart);
1503 rp->addULONG(timerStop);
1507 char* value = config.getValueString("ResumeData", (char*)data);
1510 resumePoint = strtoul(value, NULL, 10);
1513 log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1515 rp->addULONG(resumePoint);
1520 #if VDRVERSNUM < 10300
1521 summary = (char*)recording->Summary();
1523 const cRecordingInfo *Info = recording->Info();
1524 summary = (char*)Info->ShortText();
1525 if (isempty(summary)) summary = (char*)Info->Description();
1527 log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1530 rp->addString(summary);
1539 #if VDRVERSNUM < 10300
1541 // Send 0 for numchannels - this signals the client this info is not available
1545 const cComponents* components = Info->Components();
1547 log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1555 rp->addULONG(components->NumComponents());
1557 tComponent* component;
1558 for (int i = 0; i < components->NumComponents(); i++)
1560 component = components->Component(i);
1562 log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1564 rp->addUCHAR(component->stream);
1565 rp->addUCHAR(component->type);
1567 if (component->language)
1569 rp->addString(component->language);
1575 if (component->description)
1577 rp->addString(component->description);
1591 tcp.sendPacket(rp->getPtr(), rp->getLen());
1593 log->log("Client", Log::DEBUG, "Written getrecinfo");
1603 int MVPClient::processReScanRecording(UCHAR* data, int length, ResponsePacket* rp)
1607 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1613 rp->addULLONG(recplayer->getLengthBytes());
1614 rp->addULONG(recplayer->getLengthFrames());
1616 tcp.sendPacket(rp->getPtr(), rp->getLen());
1617 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1621 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1624 int MVPClient::processGetMarks(UCHAR* data, int length, ResponsePacket* rp)
1626 // data is a pointer to the fileName string
1629 cRecordings Recordings;
1630 Recordings.Load(); // probably have to do this
1632 cRecording *recording = Recordings.GetByName((char*)data);
1634 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1638 Marks.Load(recording->FileName());
1641 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1643 log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1645 rp->addULONG(m->position);
1650 log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1656 tcp.sendPacket(rp->getPtr(), rp->getLen());
1658 log->log("Client", Log::DEBUG, "Written Marks list");
1663 #endif //VOMPSTANDALONE
1666 * media List Request:
1668 * 4 VDR_GETMEDIALIST
1669 * 4 flags (currently unused)
1672 * Media List response:
1680 * 4 strlen (incl. 0 Byte)
1685 int MVPClient::processGetMediaList(UCHAR* data, int length, ResponsePacket* rp)
1688 log->log("Client", Log::ERR, "getMediaList packet too short %d", length);
1691 char * dirname=NULL;
1693 //we have a dirname provided
1694 dirname=(char *)&data[4];
1695 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
1698 MediaList * ml=MediaList::readList(baseConfig,dirname);
1700 log->log("Client", Log::ERR, "getMediaList returned NULL");
1704 //response code (not yet set)
1708 rp->addULONG(ml->size());
1710 for (MediaList::iterator nm=ml->begin(); nm<ml->end(); nm++)
1713 log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
1714 rp->addULONG(m->getType());
1716 rp->addULONG(m->getTime());
1719 int len=strlen(m->getFilename());
1721 rp->addULONG(len+1);
1722 rp->addString(m->getFilename());
1727 tcp.sendPacket(rp->getPtr(), rp->getLen());
1729 log->log("Client", Log::DEBUG, "Written Media list");
1734 * get image Request:
1735 * 4 flags (currently unused)
1740 * get image response:
1746 int MVPClient::processGetPicture(UCHAR* data, int length, ResponsePacket* rp)
1749 log->log("Client", Log::ERR, "getPicture packet too short %d", length);
1756 char * filename=NULL;
1758 //we have a dirname provided
1759 filename=(char *)&data[12];
1760 log->log("Client", Log::DEBUG, "getPicture %s", filename);
1763 log->log("Client", Log::ERR, "getPicture empty filename");
1766 imageFile=fopen(filename,"r");
1767 if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename);
1772 if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size;
1774 //response code (not yet set)
1779 log->log("Client", Log::DEBUG, "getPicture size %u", size);
1782 tcp.sendPacket(rp->getPtr(), rp->getLen());
1784 log->log("Client", Log::DEBUG, "Written getPicture");
1790 int MVPClient::processGetImageBlock(UCHAR* data, int length, ResponsePacket* rp)
1794 log->log("Client", Log::DEBUG, "Get image block called when no image active");
1798 ULLONG position = ntohll(*(ULLONG*)data);
1799 data += sizeof(ULLONG);
1800 ULONG amount = ntohl(*(ULONG*)data);
1802 log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
1804 UCHAR sendBuffer[amount];
1805 ULONG amountReceived = 0; // compiler moan.
1806 ULLONG cpos=ftell(imageFile);
1807 if (position != cpos) {
1808 fseek(imageFile,position-cpos,SEEK_CUR);
1810 if (position != (ULLONG)ftell(imageFile)) {
1811 log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position);
1814 amountReceived=fread(&sendBuffer[0],1,amount,imageFile);
1817 if (!amountReceived)
1820 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
1824 rp->copyin(sendBuffer, amount);
1825 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
1829 tcp.sendPacket(rp->getPtr(), rp->getLen());
1835 int MVPClient::processGetLanguageList(UCHAR* data, int length, ResponsePacket* rp)
1837 i18n.findLanguages();
1838 const I18n::lang_code_list& languages = i18n.getLanguageList();
1840 I18n::lang_code_list::const_iterator iter;
1841 for (iter = languages.begin(); iter != languages.end(); ++iter)
1843 rp->addString(iter->first.c_str());
1844 rp->addString(iter->second.c_str());
1847 tcp.sendPacket(rp->getPtr(), rp->getLen());
1851 int MVPClient::processGetLanguageContent(UCHAR* data, int length, ResponsePacket* rp)
1853 if (length <= 0) return 0;
1854 std::string code, result;
1855 code.assign((char*)data, length - 1);
1856 i18n.findLanguages();
1857 I18n::trans_table texts = i18n.getLanguageContent(code);
1858 I18n::trans_table::const_iterator iter;
1859 for (iter = texts.begin(); iter != texts.end(); ++iter)
1861 rp->addString(iter->first.c_str());
1862 rp->addString(iter->second.c_str());
1865 tcp.sendPacket(rp->getPtr(), rp->getLen());
1883 event = Schedule->GetPresentEvent();
1885 fprintf(f, "\n\nCurrent event\n\n");
1887 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1888 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1889 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1890 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1892 event = Schedule->GetFollowingEvent();
1894 fprintf(f, "\n\nFollowing event\n\n");
1896 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1897 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1898 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1899 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1903 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1904 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1905 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1906 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1912 void MVPClient::test2()
1914 FILE* f = fopen("/tmp/s.txt", "w");
1916 #if VDRVERSNUM < 10300
1917 cMutexLock MutexLock;
1918 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1920 cSchedulesLock MutexLock;
1921 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1926 fprintf(f, "Schedules = NULL\n");
1931 fprintf(f, "Schedules dump:\n");
1935 const cSchedule *Schedule;
1936 int scheduleNumber = 0;
1939 cChannel *thisChannel;
1941 #if VDRVERSNUM < 10300
1942 const cEventInfo *event;
1943 int eventNumber = 0;
1945 const cEvent *event;
1948 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1949 // Schedule = Schedules->GetSchedule();
1950 Schedule = Schedules->First();
1953 fprintf(f, "First Schedule = NULL\n");
1960 fprintf(f, "Schedule #%i\n", scheduleNumber);
1961 fprintf(f, "-------------\n\n");
1963 #if VDRVERSNUM < 10300
1964 tchid = Schedule->GetChannelID();
1966 tchid = Schedule->ChannelID();
1969 #if VDRVERSNUM < 10300
1970 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1971 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1973 // put the count at the end.
1976 thisChannel = Channels.GetByChannelID(tchid, true);
1979 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1983 fprintf(f, "thisChannel = NULL for tchid\n");
1986 #if VDRVERSNUM < 10300
1987 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1989 event = Schedule->GetEventNumber(eventNumber);
1990 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1991 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1992 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1993 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1994 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1995 fprintf(f, "Event %u dump:\n", eventNumber);
2000 // This whole section needs rewriting to walk the list.
2001 event = Schedule->Events()->First();
2003 event = Schedule->Events()->Next(event);
2008 fprintf(f, "\nDump from object:\n");
2010 fprintf(f, "\nEND\n");
2015 fprintf(f, "End of current Schedule\n\n\n");
2017 Schedule = (const cSchedule *)Schedules->Next(Schedule);
2025 const cEventInfo *GetPresentEvent(void) const;
2026 const cEventInfo *GetFollowingEvent(void) const;
2027 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
2028 const cEventInfo *GetEventAround(time_t tTime) const;
2029 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
2032 const unsigned char GetTableID(void) const;
2033 const char *GetTimeString(void) const;
2034 const char *GetEndTimeString(void) const;
2035 const char *GetDate(void) const;
2036 bool IsFollowing(void) const;
2037 bool IsPresent(void) const;
2038 const char *GetExtendedDescription(void) const;
2039 const char *GetSubtitle(void) const;
2040 const char *GetTitle(void) const;
2041 unsigned short GetEventID(void) const;
2042 long GetDuration(void) const;
2043 time_t GetTime(void) const;
2044 tChannelID GetChannelID(void) const;
2045 int GetChannelNumber(void) const { return nChannelNumber; }
2046 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
2047 void Dump(FILE *f, const char *Prefix = "") const;
2051 void MVPClient::test(int channelNumber)
2053 FILE* f = fopen("/tmp/test.txt", "w");
2055 cMutexLock MutexLock;
2056 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
2060 fprintf(f, "Schedules = NULL\n");
2065 fprintf(f, "Schedules dump:\n");
2066 // Schedules->Dump(f);
2068 const cSchedule *Schedule;
2069 cChannel *thisChannel;
2070 const cEventInfo *event;
2072 thisChannel = channelFromNumber(channelNumber);
2075 fprintf(f, "thisChannel = NULL\n");
2080 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
2081 // Schedule = Schedules->GetSchedule();
2082 // Schedule = Schedules->First();
2085 fprintf(f, "First Schedule = NULL\n");
2090 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
2092 // For some channels VDR seems to pick a random point in time to
2093 // start dishing out events, but they are in order
2094 // at some point in the list the time snaps to the current event
2099 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
2101 event = Schedule->GetEventNumber(eventNumber);
2102 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
2103 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
2104 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
2108 fprintf(f, "\nEND\n");
2117 Schedules = the collection of all the Schedule objects
2118 Schedule = One schedule, contants all the events for a channel
2119 Event = One programme
2128 Subtitle (used for "Programmes resume at ...")
2131 IsPresent ? easy to work out tho. Oh it doesn't always work
2134 void MVPClient::test2()
2136 log->log("-", Log::DEBUG, "Timers List");
2138 for (int i = 0; i < Timers.Count(); i++)
2140 cTimer *timer = Timers.Get(i);
2141 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
2142 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
2143 #if VDRVERSNUM < 10300
2144 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());
2146 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());
2148 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
2149 log->log("-", Log::DEBUG, "");
2152 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
2153 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
2154 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
2158 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
2159 recording is a bool, 0 for not currently recording, 1 for currently recording
2160 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