2 Copyright 2004-2005 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "mvpclient.h"
23 #include "responsepacket.h"
25 // This is here else it causes compile errors with something in libdvbmpeg
26 //#include <vdr/menu.h>
28 pthread_mutex_t threadClientMutex;
29 int MVPClient::nr_clients = 0;
32 MVPClient::MVPClient(Config* cfgBase, char* tconfigDir, int tsocket)
33 : tcp(tsocket), i18n(tconfigDir)
35 #ifndef VOMPSTANDALONE
38 recordingManager = NULL;
41 log = Log::getInstance();
43 configDir = tconfigDir;
44 log->log("Client", Log::DEBUG, "Config dir: %s", configDir);
49 MVPClient::~MVPClient()
51 log->log("Client", Log::DEBUG, "MVP client destructor");
52 #ifndef VOMPSTANDALONE
63 delete recordingManager;
65 recordingManager = NULL;
68 if (loggedIn) cleanConfig();
72 void MVPClient::incClients()
74 pthread_mutex_lock(&threadClientMutex);
75 MVPClient::nr_clients++;
76 pthread_mutex_unlock(&threadClientMutex);
79 void MVPClient::decClients()
81 pthread_mutex_lock(&threadClientMutex);
82 MVPClient::nr_clients--;
83 pthread_mutex_unlock(&threadClientMutex);
86 int MVPClient::getNrClients()
89 pthread_mutex_lock(&threadClientMutex);
90 nrClients = MVPClient::nr_clients;
91 pthread_mutex_unlock(&threadClientMutex);
95 ULLONG MVPClient::ntohll(ULLONG a)
100 ULLONG MVPClient::htonll(ULLONG a)
102 #if BYTE_ORDER == BIG_ENDIAN
107 b = ((a << 56) & 0xFF00000000000000ULL)
108 | ((a << 40) & 0x00FF000000000000ULL)
109 | ((a << 24) & 0x0000FF0000000000ULL)
110 | ((a << 8) & 0x000000FF00000000ULL)
111 | ((a >> 8) & 0x00000000FF000000ULL)
112 | ((a >> 24) & 0x0000000000FF0000ULL)
113 | ((a >> 40) & 0x000000000000FF00ULL)
114 | ((a >> 56) & 0x00000000000000FFULL) ;
120 #ifndef VOMPSTANDALONE
121 cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
123 cChannel* channel = NULL;
125 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
127 if (!channel->GroupSep())
129 log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());
131 if (channel->Number() == (int)channelNumber)
133 int vpid = channel->Vpid();
134 #if VDRVERSNUM < 10300
135 int apid1 = channel->Apid1();
137 int apid1 = channel->Apid(0);
139 log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
147 log->log("Client", Log::DEBUG, "Channel not found");
153 void MVPClient::writeResumeData()
155 config.setValueLong("ResumeData",
156 (char*)recplayer->getCurrentRecording()->FileName(),
157 recplayer->frameNumberFromPosition(recplayer->getLastPosition()) );
161 void MVPClient::cleanConfig()
163 log->log("Client", Log::DEBUG, "Clean config");
165 #ifndef VOMPSTANDALONE
167 cRecordings Recordings;
172 char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
173 char* position = resumes;
174 for(int k = 0; k < numReturns; k++)
176 log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);
178 cRecording* recording = Recordings.GetByName(position);
181 // doesn't exist anymore
182 log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
183 config.deleteValue("ResumeData", position);
187 log->log("Client", Log::DEBUG, "This recording still exists");
190 position += strlen(position) + 1;
197 void MVPClientStartThread(void* arg)
199 MVPClient* m = (MVPClient*)arg;
201 // Nothing external to this class has a reference to it
202 // This is the end of the thread.. so delete m
209 if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
210 log->log("Client", Log::DEBUG, "MVPClient run success");
214 void MVPClient::run2()
219 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
220 pthread_detach(runThread); // Detach
222 tcp.disableReadTimeout();
224 tcp.setSoKeepTime(3);
225 tcp.setNonBlocking();
230 ULONG extraDataLength;
236 log->log("Client", Log::DEBUG, "Waiting");
239 if (!tcp.readData((UCHAR*)&channelID, sizeof(ULONG))) break;
240 channelID = ntohl(channelID);
243 log->log("Client", Log::ERR, "Incoming channel number not 1!");
247 log->log("Client", Log::DEBUG, "Got chan");
249 if (!tcp.readData((UCHAR*)&requestID, sizeof(ULONG))) break;
250 requestID = ntohl(requestID);
252 log->log("Client", Log::DEBUG, "Got ser");
254 if (!tcp.readData((UCHAR*)&opcode, sizeof(ULONG))) break;
255 opcode = ntohl(opcode);
257 log->log("Client", Log::DEBUG, "Got op %lu", opcode);
259 if (!tcp.readData((UCHAR*)&extraDataLength, sizeof(ULONG))) break;
260 extraDataLength = ntohl(extraDataLength);
261 if (extraDataLength > 200000)
263 log->log("Client", Log::ERR, "ExtraDataLength > 200000!");
267 log->log("Client", Log::DEBUG, "Got edl %lu", extraDataLength);
271 data = (UCHAR*)malloc(extraDataLength);
274 log->log("Client", Log::ERR, "Extra data buffer malloc error");
278 if (!tcp.readData(data, extraDataLength))
280 log->log("Client", Log::ERR, "Could not read extradata");
290 log->log("Client", Log::DEBUG, "Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, extraDataLength);
292 if (!loggedIn && (opcode != 1))
294 log->log("Client", Log::ERR, "Not logged in and opcode != 1");
295 if (data) free(data);
299 ResponsePacket* rp = new ResponsePacket();
300 if (!rp->init(requestID))
302 log->log("Client", Log::ERR, "response packet init fail");
310 result = processLogin(data, extraDataLength, rp);
312 #ifndef VOMPSTANDALONE
314 result = processGetRecordingsList(data, extraDataLength, rp);
317 result = processDeleteRecording(data, extraDataLength, rp);
320 result = processGetChannelsList(data, extraDataLength, rp);
323 result = processStartStreamingChannel(data, extraDataLength, requestID, rp);
326 result = processGetBlock(data, extraDataLength, rp);
329 result = processStopStreaming(data, extraDataLength, rp);
332 result = processStartStreamingRecording(data, extraDataLength, rp);
336 result = processGetChannelSchedule(data, extraDataLength, rp);
339 result = processConfigSave(data, extraDataLength, rp);
342 result = processConfigLoad(data, extraDataLength, rp);
344 #ifndef VOMPSTANDALONE
346 result = processReScanRecording(data, extraDataLength, rp); // FIXME obselete
349 result = processGetTimers(data, extraDataLength, rp);
352 result = processSetTimer(data, extraDataLength, rp);
355 result = processPositionFromFrameNumber(data, extraDataLength, rp);
358 result = processFrameNumberFromPosition(data, extraDataLength, rp);
361 result = processMoveRecording(data, extraDataLength, rp);
364 result = processGetIFrame(data, extraDataLength, rp);
367 result = processGetRecInfo(data, extraDataLength, rp);
370 result = processGetMarks(data, extraDataLength, rp);
373 result = processGetChannelPids(data, extraDataLength, rp);
376 result = processDeleteTimer(data, extraDataLength, rp);
380 result = processGetMediaList(data, extraDataLength, rp);
383 result = processGetPicture(data, extraDataLength, rp);
386 result = processGetImageBlock(data, extraDataLength, rp);
389 result = processGetLanguageList(data, extraDataLength, rp);
392 result = processGetLanguageContent(data, extraDataLength, rp);
397 if (data) free(data);
402 int MVPClient::processLogin(UCHAR* buffer, int length, ResponsePacket* rp)
404 if (length != 6) return 0;
408 char configFileName[PATH_MAX];
409 snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", configDir, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
410 config.init(configFileName);
412 // Send the login reply
414 time_t timeNow = time(NULL);
415 struct tm* timeStruct = localtime(&timeNow);
416 int timeOffset = timeStruct->tm_gmtoff;
418 rp->addULONG(timeNow);
419 rp->addLONG(timeOffset);
421 tcp.sendPacket(rp->getPtr(), rp->getLen());
422 log->log("Client", Log::DEBUG, "written login reply len %lu", rp->getLen());
428 #ifndef VOMPSTANDALONE
429 int MVPClient::processGetRecordingsList(UCHAR* data, int length, ResponsePacket* rp)
432 int Percent = VideoDiskSpace(&FreeMB);
433 int Total = (FreeMB / (100 - Percent)) * 100;
436 rp->addULONG(FreeMB);
437 rp->addULONG(Percent);
439 cRecordings Recordings;
442 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
444 rp->addULONG(recording->start);
445 rp->addString(recording->Name());
446 rp->addString(recording->FileName());
450 tcp.sendPacket(rp->getPtr(), rp->getLen());
452 log->log("Client", Log::DEBUG, "Written recordings list");
457 int MVPClient::processDeleteRecording(UCHAR* data, int length, ResponsePacket* rp)
459 // data is a pointer to the fileName string
461 cRecordings Recordings;
462 Recordings.Load(); // probably have to do this
464 cRecording* recording = Recordings.GetByName((char*)data);
466 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
470 log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());
472 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
475 if (recording->Delete())
477 // Copy svdrp's way of doing this, see if it works
478 #if VDRVERSNUM > 10300
479 ::Recordings.DelByName(recording->FileName());
499 tcp.sendPacket(rp->getPtr(), rp->getLen());
504 int MVPClient::processMoveRecording(UCHAR* data, int length, ResponsePacket* rp)
506 log->log("Client", Log::DEBUG, "Process move recording");
507 char* fileName = (char*)data;
508 char* newPath = NULL;
510 for (int k = 0; k < length; k++)
514 newPath = (char*)&data[k+1];
518 if (!newPath) return 0;
520 cRecordings Recordings;
521 Recordings.Load(); // probably have to do this
523 cRecording* recording = Recordings.GetByName((char*)fileName);
525 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
529 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
532 log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
533 log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
534 log->log("Client", Log::DEBUG, "to: %s", newPath);
536 const char* t = recording->FileName();
538 char* dateDirName = NULL; int k;
539 char* titleDirName = NULL; int j;
541 // Find the datedirname
542 for(k = strlen(t) - 1; k >= 0; k--)
546 log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
547 dateDirName = new char[strlen(&t[k+1]) + 1];
548 strcpy(dateDirName, &t[k+1]);
553 // Find the titledirname
555 for(j = k-1; j >= 0; j--)
559 log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
560 titleDirName = new char[(k - j - 1) + 1];
561 memcpy(titleDirName, &t[j+1], k - j - 1);
562 titleDirName[k - j - 1] = '\0';
567 log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
568 log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);
569 log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);
571 char* newPathConv = new char[strlen(newPath)+1];
572 strcpy(newPathConv, newPath);
573 ExchangeChars(newPathConv, true);
574 log->log("Client", Log::DEBUG, "EC: %s", newPathConv);
576 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
577 log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
578 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
579 delete[] newPathConv;
581 log->log("Client", Log::DEBUG, "%s", newContainer);
584 int statret = stat(newContainer, &dstat);
585 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
587 log->log("Client", Log::DEBUG, "new dir does not exist");
588 int mkdirret = mkdir(newContainer, 0755);
591 delete[] dateDirName;
592 delete[] titleDirName;
593 delete[] newContainer;
597 tcp.sendPacket(rp->getPtr(), rp->getLen());
601 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
603 delete[] dateDirName;
604 delete[] titleDirName;
605 delete[] newContainer;
609 tcp.sendPacket(rp->getPtr(), rp->getLen());
613 // Ok, the directory container has been made, or it pre-existed.
615 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
616 sprintf(newDir, "%s/%s", newContainer, dateDirName);
618 log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
619 int renameret = rename(t, newDir);
622 // Success. Test for remove old dir containter
623 char* oldTitleDir = new char[k+1];
624 memcpy(oldTitleDir, t, k);
625 oldTitleDir[k] = '\0';
626 log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
627 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
628 delete[] oldTitleDir;
633 #if VDRVERSNUM > 10311
635 ::Recordings.Update();
637 // Success. Send a different packet from just a ulong
638 rp->addULONG(1); // success
639 rp->addString(newDir);
647 tcp.sendPacket(rp->getPtr(), rp->getLen());
649 delete[] dateDirName;
650 delete[] titleDirName;
651 delete[] newContainer;
658 tcp.sendPacket(rp->getPtr(), rp->getLen());
665 tcp.sendPacket(rp->getPtr(), rp->getLen());
671 int MVPClient::processGetChannelsList(UCHAR* data, int length, ResponsePacket* rp)
675 char* chanConfig = config.getValueString("General", "Channels");
677 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
679 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
681 #if VDRVERSNUM < 10300
682 if (!channel->GroupSep() && (!channel->Ca() || allChans))
684 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
687 log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());
689 if (channel->Vpid()) type = 1;
690 #if VDRVERSNUM < 10300
693 else if (channel->Apid(0)) type = 2;
697 rp->addULONG(channel->Number());
699 rp->addString(channel->Name());
704 tcp.sendPacket(rp->getPtr(), rp->getLen());
706 log->log("Client", Log::DEBUG, "Written channels list");
711 int MVPClient::processGetChannelPids(UCHAR* data, int length, ResponsePacket* rp)
713 ULONG channelNumber = ntohl(*(ULONG*)data);
715 cChannel* channel = channelFromNumber(channelNumber);
720 tcp.sendPacket(rp->getPtr(), rp->getLen());
726 #if VDRVERSNUM < 10300
728 log->log("Client", Log::DEBUG, "Apid1: %i", channel->Apid1());
729 log->log("Client", Log::DEBUG, "Apid2: %i", channel->Apid2());
731 if (channel->Apid2())
733 else if (channel->Apid1())
740 for (const int *Apid = channel->Apids(); *Apid; Apid++)
747 // Format of response
755 rp->addULONG(channel->Vpid());
756 rp->addULONG(numApids);
758 #if VDRVERSNUM < 10300
761 rp->addULONG(channel->Apid1());
766 rp->addULONG(channel->Apid2());
770 for (ULONG i = 0; i < numApids; i++)
772 rp->addULONG(channel->Apid(i));
773 rp->addString(channel->Alang(i));
778 tcp.sendPacket(rp->getPtr(), rp->getLen());
780 log->log("Client", Log::DEBUG, "Written channels pids");
785 int MVPClient::processStartStreamingChannel(UCHAR* data, int length, ULONG streamID, ResponsePacket* rp)
789 log->log("Client", Log::ERR, "Client called start streaming twice");
793 log->log("Client", Log::DEBUG, "length = %i", length);
794 ULONG channelNumber = ntohl(*(ULONG*)data);
796 cChannel* channel = channelFromNumber(channelNumber);
801 tcp.sendPacket(rp->getPtr(), rp->getLen());
805 // get the priority we should use
807 int priority = config.getValueLong("General", "Live priority", &fail);
810 log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
814 log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
819 if (priority < 0) priority = 0;
820 if (priority > 99) priority = 99;
822 log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
823 lp = MVPReceiver::create(channel, priority);
829 tcp.sendPacket(rp->getPtr(), rp->getLen());
833 if (!lp->init(&tcp, streamID))
839 tcp.sendPacket(rp->getPtr(), rp->getLen());
845 tcp.sendPacket(rp->getPtr(), rp->getLen());
849 int MVPClient::processStopStreaming(UCHAR* data, int length, ResponsePacket* rp)
851 log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
862 delete recordingManager;
864 recordingManager = NULL;
869 tcp.sendPacket(rp->getPtr(), rp->getLen());
873 int MVPClient::processGetBlock(UCHAR* data, int length, ResponsePacket* rp)
875 if (!lp && !recplayer)
877 log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
881 ULLONG position = ntohll(*(ULLONG*)data);
882 data += sizeof(ULLONG);
883 ULONG amount = ntohl(*(ULONG*)data);
885 log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
887 UCHAR sendBuffer[amount];
888 ULONG amountReceived = 0; // compiler moan.
891 log->log("Client", Log::DEBUG, "getting from live");
892 amountReceived = lp->getBlock(&sendBuffer[0], amount);
896 // vdr has possibly disconnected the receiver
897 log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
904 log->log("Client", Log::DEBUG, "getting from recording");
905 amountReceived = recplayer->getBlock(&sendBuffer[0], position, amount);
911 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
915 rp->copyin(sendBuffer, amountReceived);
916 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
920 tcp.sendPacket(rp->getPtr(), rp->getLen());
921 log->log("Client", Log::DEBUG, "Finished getblock, have sent %lu", rp->getLen());
925 int MVPClient::processStartStreamingRecording(UCHAR* data, int length, ResponsePacket* rp)
927 // data is a pointer to the fileName string
929 recordingManager = new cRecordings;
930 recordingManager->Load();
932 cRecording* recording = recordingManager->GetByName((char*)data);
934 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
938 recplayer = new RecPlayer(recording);
940 rp->addULLONG(recplayer->getLengthBytes());
941 rp->addULONG(recplayer->getLengthFrames());
943 tcp.sendPacket(rp->getPtr(), rp->getLen());
945 log->log("Client", Log::DEBUG, "written totalLength");
949 delete recordingManager;
950 recordingManager = NULL;
955 int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length, ResponsePacket* rp)
959 ULONG frameNumber = ntohl(*(ULONG*)data);
964 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
968 retval = recplayer->positionFromFrameNumber(frameNumber);
971 rp->addULLONG(retval);
973 tcp.sendPacket(rp->getPtr(), rp->getLen());
975 log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
979 int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length, ResponsePacket* rp)
983 ULLONG position = ntohll(*(ULLONG*)data);
988 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
992 retval = recplayer->frameNumberFromPosition(position);
995 rp->addULONG(retval);
997 tcp.sendPacket(rp->getPtr(), rp->getLen());
999 log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1003 int MVPClient::processGetIFrame(UCHAR* data, int length, ResponsePacket* rp)
1005 bool success = false;
1007 ULONG frameNumber = ntohl(*(ULONG*)data);
1009 ULONG direction = ntohl(*(ULONG*)data);
1012 ULLONG rfilePosition = 0;
1013 ULONG rframeNumber = 0;
1014 ULONG rframeLength = 0;
1018 log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1022 success = recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1025 // returns file position, frame number, length
1029 rp->addULLONG(rfilePosition);
1030 rp->addULONG(rframeNumber);
1031 rp->addULONG(rframeLength);
1039 tcp.sendPacket(rp->getPtr(), rp->getLen());
1041 log->log("Client", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1045 int MVPClient::processGetChannelSchedule(UCHAR* data, int length, ResponsePacket* rp)
1047 ULONG channelNumber = ntohl(*(ULONG*)data);
1049 ULONG startTime = ntohl(*(ULONG*)data);
1051 ULONG duration = ntohl(*(ULONG*)data);
1053 log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1055 cChannel* channel = channelFromNumber(channelNumber);
1060 tcp.sendPacket(rp->getPtr(), rp->getLen());
1062 log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
1066 log->log("Client", Log::DEBUG, "Got channel");
1068 #if VDRVERSNUM < 10300
1069 cMutexLock MutexLock;
1070 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1072 cSchedulesLock MutexLock;
1073 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1079 tcp.sendPacket(rp->getPtr(), rp->getLen());
1081 log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1085 log->log("Client", Log::DEBUG, "Got schedule!s! object");
1087 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1092 tcp.sendPacket(rp->getPtr(), rp->getLen());
1094 log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
1098 log->log("Client", Log::DEBUG, "Got schedule object");
1100 const char* empty = "";
1101 bool atLeastOneEvent = false;
1104 ULONG thisEventTime;
1105 ULONG thisEventDuration;
1106 const char* thisEventTitle;
1107 const char* thisEventSubTitle;
1108 const char* thisEventDescription;
1110 #if VDRVERSNUM < 10300
1112 const cEventInfo *event;
1113 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1115 event = Schedule->GetEventNumber(eventNumber);
1117 thisEventID = event->GetEventID();
1118 thisEventTime = event->GetTime();
1119 thisEventDuration = event->GetDuration();
1120 thisEventTitle = event->GetTitle();
1121 thisEventSubTitle = event->GetSubtitle();
1122 thisEventDescription = event->GetExtendedDescription();
1126 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1128 thisEventID = event->EventID();
1129 thisEventTime = event->StartTime();
1130 thisEventDuration = event->Duration();
1131 thisEventTitle = event->Title();
1132 thisEventSubTitle = NULL;
1133 thisEventDescription = event->Description();
1137 log->log("Client", Log::DEBUG, "Got an event object %p", event);
1139 //in the past filter
1140 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1143 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1146 if (thisEventTime >= (startTime + duration)) continue;
1148 if (!thisEventTitle) thisEventTitle = empty;
1149 if (!thisEventSubTitle) thisEventSubTitle = empty;
1150 if (!thisEventDescription) thisEventDescription = empty;
1152 rp->addULONG(thisEventID);
1153 rp->addULONG(thisEventTime);
1154 rp->addULONG(thisEventDuration);
1156 rp->addString(thisEventTitle);
1157 rp->addString(thisEventSubTitle);
1158 rp->addString(thisEventDescription);
1160 atLeastOneEvent = true;
1161 log->log("Client", Log::DEBUG, "Done s3");
1164 log->log("Client", Log::DEBUG, "Got all event data");
1166 if (!atLeastOneEvent)
1169 log->log("Client", Log::DEBUG, "Written 0 because no data");
1173 tcp.sendPacket(rp->getPtr(), rp->getLen());
1175 log->log("Client", Log::DEBUG, "written schedules packet");
1180 #endif //VOMPSTANDALONE
1182 int MVPClient::processConfigSave(UCHAR* buffer, int length, ResponsePacket* rp)
1184 char* section = (char*)buffer;
1188 for (int k = 0; k < length; k++)
1190 if (buffer[k] == '\0')
1194 key = (char*)&buffer[k+1];
1198 value = (char*)&buffer[k+1];
1204 // if the last string (value) doesnt have null terminator, give up
1205 if (buffer[length - 1] != '\0') return 0;
1207 log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
1208 if (config.setValueString(section, key, value))
1218 tcp.sendPacket(rp->getPtr(), rp->getLen());
1223 int MVPClient::processConfigLoad(UCHAR* buffer, int length, ResponsePacket* rp)
1225 char* section = (char*)buffer;
1228 for (int k = 0; k < length; k++)
1230 if (buffer[k] == '\0')
1232 key = (char*)&buffer[k+1];
1237 char* value = config.getValueString(section, key);
1241 rp->addString(value);
1242 log->log("Client", Log::DEBUG, "Written config load packet");
1248 log->log("Client", Log::DEBUG, "Written config load failed packet");
1252 tcp.sendPacket(rp->getPtr(), rp->getLen());
1257 #ifndef VOMPSTANDALONE
1259 int MVPClient::processGetTimers(UCHAR* buffer, int length, ResponsePacket* rp)
1262 int numTimers = Timers.Count();
1264 rp->addULONG(numTimers);
1266 for (int i = 0; i < numTimers; i++)
1268 timer = Timers.Get(i);
1270 #if VDRVERSNUM < 10300
1271 rp->addULONG(timer->Active());
1273 rp->addULONG(timer->HasFlags(tfActive));
1275 rp->addULONG(timer->Recording());
1276 rp->addULONG(timer->Pending());
1277 rp->addULONG(timer->Priority());
1278 rp->addULONG(timer->Lifetime());
1279 rp->addULONG(timer->Channel()->Number());
1280 rp->addULONG(timer->StartTime());
1281 rp->addULONG(timer->StopTime());
1282 rp->addULONG(timer->Day());
1283 rp->addULONG(timer->WeekDays());
1284 rp->addString(timer->File());
1288 tcp.sendPacket(rp->getPtr(), rp->getLen());
1290 log->log("Client", Log::DEBUG, "Written timers list");
1295 int MVPClient::processSetTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1297 char* timerString = new char[strlen((char*)buffer) + 1];
1298 strcpy(timerString, (char*)buffer);
1300 #if VDRVERSNUM < 10300
1302 // If this is VDR 1.2 the date part of the timer string must be reduced
1303 // to just DD rather than YYYY-MM-DD
1305 int s = 0; // source
1306 int d = 0; // destination
1308 while(c != 2) // copy up to date section, including the second ':'
1310 timerString[d] = buffer[s];
1311 if (buffer[s] == ':') c++;
1315 // now it has copied up to the date section
1317 while(c != 2) // waste YYYY-MM-
1319 if (buffer[s] == '-') c++;
1322 // now source is at the DD
1323 memcpy(&timerString[d], &buffer[s], length - s);
1325 timerString[d] = '\0';
1327 log->log("Client", Log::DEBUG, "Timer string after 1.2 conversion:");
1328 log->log("Client", Log::DEBUG, "%s", timerString);
1332 cTimer *timer = new cTimer;
1333 if (timer->Parse((char*)timerString))
1335 cTimer *t = Timers.GetTimer(timer);
1339 #if VDRVERSNUM < 10300
1342 Timers.SetModified();
1346 tcp.sendPacket(rp->getPtr(), rp->getLen());
1353 tcp.sendPacket(rp->getPtr(), rp->getLen());
1360 tcp.sendPacket(rp->getPtr(), rp->getLen());
1366 int MVPClient::processDeleteTimer(UCHAR* buffer, int length, ResponsePacket* rp)
1368 log->log("Client", Log::DEBUG, "Delete timer called");
1373 INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4;
1374 INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4;
1375 INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4;
1376 INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4;
1377 INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4;
1380 for (ti = Timers.First(); ti; ti = Timers.Next(ti))
1382 if ( (ti->Channel()->Number() == delChannel)
1383 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1384 && (ti->StartTime() == delStart)
1385 && (ti->StopTime() == delStop) )
1393 tcp.sendPacket(rp->getPtr(), rp->getLen());
1397 if (!Timers.BeingEdited())
1399 if (!ti->Recording())
1402 Timers.SetModified();
1405 tcp.sendPacket(rp->getPtr(), rp->getLen());
1410 log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
1413 tcp.sendPacket(rp->getPtr(), rp->getLen());
1419 log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1422 tcp.sendPacket(rp->getPtr(), rp->getLen());
1427 int MVPClient::processGetRecInfo(UCHAR* data, int length, ResponsePacket* rp)
1429 // data is a pointer to the fileName string
1431 cRecordings Recordings;
1432 Recordings.Load(); // probably have to do this
1434 cRecording *recording = Recordings.GetByName((char*)data);
1436 time_t timerStart = 0;
1437 time_t timerStop = 0;
1438 char* summary = NULL;
1439 ULONG resumePoint = 0;
1443 log->log("Client", Log::ERR, "GetRecInfo found no recording");
1446 tcp.sendPacket(rp->getPtr(), rp->getLen());
1451 4 bytes: start time for timer
1452 4 bytes: end time for timer
1453 4 bytes: resume point
1455 4 bytes: num components
1465 // Get current timer
1467 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1470 timerStart = rc->Timer()->StartTime();
1471 timerStop = rc->Timer()->StopTime();
1472 log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1475 rp->addULONG(timerStart);
1476 rp->addULONG(timerStop);
1480 char* value = config.getValueString("ResumeData", (char*)data);
1483 resumePoint = strtoul(value, NULL, 10);
1486 log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1488 rp->addULONG(resumePoint);
1493 #if VDRVERSNUM < 10300
1494 summary = (char*)recording->Summary();
1496 const cRecordingInfo *Info = recording->Info();
1497 summary = (char*)Info->ShortText();
1498 if (isempty(summary)) summary = (char*)Info->Description();
1500 log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
1503 rp->addString(summary);
1512 #if VDRVERSNUM < 10300
1514 // Send 0 for numchannels - this signals the client this info is not available
1518 const cComponents* components = Info->Components();
1520 log->log("Client", Log::DEBUG, "GRI: D1: %p", components);
1528 rp->addULONG(components->NumComponents());
1530 tComponent* component;
1531 for (int i = 0; i < components->NumComponents(); i++)
1533 component = components->Component(i);
1535 log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
1537 rp->addUCHAR(component->stream);
1538 rp->addUCHAR(component->type);
1540 if (component->language)
1542 rp->addString(component->language);
1548 if (component->description)
1550 rp->addString(component->description);
1564 tcp.sendPacket(rp->getPtr(), rp->getLen());
1566 log->log("Client", Log::DEBUG, "Written getrecinfo");
1576 int MVPClient::processReScanRecording(UCHAR* data, int length, ResponsePacket* rp)
1580 log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
1586 rp->addULLONG(recplayer->getLengthBytes());
1587 rp->addULONG(recplayer->getLengthFrames());
1589 tcp.sendPacket(rp->getPtr(), rp->getLen());
1590 log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
1594 // FIXME without client calling rescan, getblock wont work even tho more data is avail
1597 int MVPClient::processGetMarks(UCHAR* data, int length, ResponsePacket* rp)
1599 // data is a pointer to the fileName string
1602 cRecordings Recordings;
1603 Recordings.Load(); // probably have to do this
1605 cRecording *recording = Recordings.GetByName((char*)data);
1607 log->log("Client", Log::DEBUG, "recording pointer %p", recording);
1611 Marks.Load(recording->FileName());
1614 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
1616 log->log("Client", Log::DEBUG, "found Mark %i", m->position);
1618 rp->addULONG(m->position);
1623 log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
1629 tcp.sendPacket(rp->getPtr(), rp->getLen());
1631 log->log("Client", Log::DEBUG, "Written Marks list");
1636 #endif //VOMPSTANDALONE
1639 * media List Request:
1641 * 4 VDR_GETMEDIALIST
1642 * 4 flags (currently unused)
1645 * Media List response:
1653 * 4 strlen (incl. 0 Byte)
1658 int MVPClient::processGetMediaList(UCHAR* data, int length, ResponsePacket* rp)
1661 log->log("Client", Log::ERR, "getMediaList packet too short %d", length);
1664 char * dirname=NULL;
1666 //we have a dirname provided
1667 dirname=(char *)&data[4];
1668 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
1671 MediaList * ml=MediaList::readList(baseConfig,dirname);
1673 log->log("Client", Log::ERR, "getMediaList returned NULL");
1677 //response code (not yet set)
1681 rp->addULONG(ml->size());
1683 for (MediaList::iterator nm=ml->begin(); nm<ml->end(); nm++)
1686 log->log("Client", Log::DEBUG, "found media entry %s, type=%d",m->getFilename(),m->getType());
1687 rp->addULONG(m->getType());
1689 rp->addULONG(m->getTime());
1692 int len=strlen(m->getFilename());
1694 rp->addULONG(len+1);
1695 rp->addString(m->getFilename());
1700 tcp.sendPacket(rp->getPtr(), rp->getLen());
1702 log->log("Client", Log::DEBUG, "Written Media list");
1707 * get image Request:
1708 * 4 flags (currently unused)
1713 * get image response:
1719 int MVPClient::processGetPicture(UCHAR* data, int length, ResponsePacket* rp)
1722 log->log("Client", Log::ERR, "getPicture packet too short %d", length);
1729 char * filename=NULL;
1731 //we have a dirname provided
1732 filename=(char *)&data[12];
1733 log->log("Client", Log::DEBUG, "getPicture %s", filename);
1736 log->log("Client", Log::ERR, "getPicture empty filename");
1739 imageFile=fopen(filename,"r");
1740 if (!imageFile) log->log("Client", Log::ERR, "getPicture unable to open %s",filename);
1745 if ( fstat(fileno(imageFile),&st) == 0) size=st.st_size;
1747 //response code (not yet set)
1752 log->log("Client", Log::DEBUG, "getPicture size %u", size);
1755 tcp.sendPacket(rp->getPtr(), rp->getLen());
1757 log->log("Client", Log::DEBUG, "Written getPicture");
1763 int MVPClient::processGetImageBlock(UCHAR* data, int length, ResponsePacket* rp)
1767 log->log("Client", Log::DEBUG, "Get image block called when no image active");
1771 ULLONG position = ntohll(*(ULLONG*)data);
1772 data += sizeof(ULLONG);
1773 ULONG amount = ntohl(*(ULONG*)data);
1775 log->log("Client", Log::DEBUG, "getImageblock pos = %llu length = %lu", position, amount);
1777 UCHAR sendBuffer[amount];
1778 ULONG amountReceived = 0; // compiler moan.
1779 ULLONG cpos=ftell(imageFile);
1780 if (position != cpos) {
1781 fseek(imageFile,position-cpos,SEEK_CUR);
1783 if (position != (ULLONG)ftell(imageFile)) {
1784 log->log("Client", Log::DEBUG, "getImageblock pos = %llu not available", position);
1787 amountReceived=fread(&sendBuffer[0],1,amount,imageFile);
1790 if (!amountReceived)
1793 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
1797 rp->copyin(sendBuffer, amount);
1798 log->log("Client", Log::DEBUG, "written %lu", amountReceived);
1802 tcp.sendPacket(rp->getPtr(), rp->getLen());
1808 int MVPClient::processGetLanguageList(UCHAR* data, int length, ResponsePacket* rp)
1810 i18n.findLanguages();
1811 const I18n::lang_code_list& languages = i18n.getLanguageList();
1813 I18n::lang_code_list::const_iterator iter;
1814 for (iter = languages.begin(); iter != languages.end(); ++iter)
1816 rp->addString(iter->first.c_str());
1817 rp->addString(iter->second.c_str());
1820 tcp.sendPacket(rp->getPtr(), rp->getLen());
1824 int MVPClient::processGetLanguageContent(UCHAR* data, int length, ResponsePacket* rp)
1826 if (length <= 0) return 0;
1827 std::string code, result;
1828 code.assign((char*)data, length - 1);
1829 i18n.findLanguages();
1830 I18n::trans_table texts = i18n.getLanguageContent(code);
1831 I18n::trans_table::const_iterator iter;
1832 for (iter = texts.begin(); iter != texts.end(); ++iter)
1834 rp->addString(iter->first.c_str());
1835 rp->addString(iter->second.c_str());
1838 tcp.sendPacket(rp->getPtr(), rp->getLen());
1856 event = Schedule->GetPresentEvent();
1858 fprintf(f, "\n\nCurrent event\n\n");
1860 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1861 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1862 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1863 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1865 event = Schedule->GetFollowingEvent();
1867 fprintf(f, "\n\nFollowing event\n\n");
1869 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", 0, event->GetEventID(), event->GetTime(), event->GetDuration());
1870 fprintf(f, "Event %i title = %s subtitle = %s\n", 0, event->GetTitle(), event->GetSubtitle());
1871 fprintf(f, "Event %i extendeddescription = %s\n", 0, event->GetExtendedDescription());
1872 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", 0, event->IsFollowing(), event->IsPresent());
1876 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
1877 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
1878 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1879 fprintf(f, "Event %i isFollowing = %i, isPresent = %i\n", eventNumber, event->IsFollowing(), event->IsPresent());
1885 void MVPClient::test2()
1887 FILE* f = fopen("/tmp/s.txt", "w");
1889 #if VDRVERSNUM < 10300
1890 cMutexLock MutexLock;
1891 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
1893 cSchedulesLock MutexLock;
1894 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1899 fprintf(f, "Schedules = NULL\n");
1904 fprintf(f, "Schedules dump:\n");
1908 const cSchedule *Schedule;
1909 int scheduleNumber = 0;
1912 cChannel *thisChannel;
1914 #if VDRVERSNUM < 10300
1915 const cEventInfo *event;
1916 int eventNumber = 0;
1918 const cEvent *event;
1921 // Schedule = Schedules->GetSchedule(channel->GetChannelID());
1922 // Schedule = Schedules->GetSchedule();
1923 Schedule = Schedules->First();
1926 fprintf(f, "First Schedule = NULL\n");
1933 fprintf(f, "Schedule #%i\n", scheduleNumber);
1934 fprintf(f, "-------------\n\n");
1936 #if VDRVERSNUM < 10300
1937 tchid = Schedule->GetChannelID();
1939 tchid = Schedule->ChannelID();
1942 #if VDRVERSNUM < 10300
1943 fprintf(f, "ChannelID.ToString() = %s\n", tchid.ToString());
1944 fprintf(f, "NumEvents() = %i\n", Schedule->NumEvents());
1946 // put the count at the end.
1949 thisChannel = Channels.GetByChannelID(tchid, true);
1952 fprintf(f, "Channel Number: %p %i\n", thisChannel, thisChannel->Number());
1956 fprintf(f, "thisChannel = NULL for tchid\n");
1959 #if VDRVERSNUM < 10300
1960 for (eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1962 event = Schedule->GetEventNumber(eventNumber);
1963 fprintf(f, "Event %i tableid = %i timestring = %s endtimestring = %s\n", eventNumber, event->GetTableID(), event->GetTimeString(), event->GetEndTimeString());
1964 fprintf(f, "Event %i date = %s isfollowing = %i ispresent = %i\n", eventNumber, event->GetDate(), event->IsFollowing(), event->IsPresent());
1965 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
1966 fprintf(f, "Event %i subtitle = %s title = %s\n", eventNumber, event->GetSubtitle(), event->GetTitle());
1967 fprintf(f, "Event %i eventid = %u duration = %li time = %lu channelnumber = %i\n", eventNumber, event->GetEventID(), event->GetDuration(), event->GetTime(), event->GetChannelNumber());
1968 fprintf(f, "Event %u dump:\n", eventNumber);
1973 // This whole section needs rewriting to walk the list.
1974 event = Schedule->Events()->First();
1976 event = Schedule->Events()->Next(event);
1981 fprintf(f, "\nDump from object:\n");
1983 fprintf(f, "\nEND\n");
1988 fprintf(f, "End of current Schedule\n\n\n");
1990 Schedule = (const cSchedule *)Schedules->Next(Schedule);
1998 const cEventInfo *GetPresentEvent(void) const;
1999 const cEventInfo *GetFollowingEvent(void) const;
2000 const cEventInfo *GetEvent(unsigned short uEventID, time_t tTime = 0) const;
2001 const cEventInfo *GetEventAround(time_t tTime) const;
2002 const cEventInfo *GetEventNumber(int n) const { return Events.Get(n); }
2005 const unsigned char GetTableID(void) const;
2006 const char *GetTimeString(void) const;
2007 const char *GetEndTimeString(void) const;
2008 const char *GetDate(void) const;
2009 bool IsFollowing(void) const;
2010 bool IsPresent(void) const;
2011 const char *GetExtendedDescription(void) const;
2012 const char *GetSubtitle(void) const;
2013 const char *GetTitle(void) const;
2014 unsigned short GetEventID(void) const;
2015 long GetDuration(void) const;
2016 time_t GetTime(void) const;
2017 tChannelID GetChannelID(void) const;
2018 int GetChannelNumber(void) const { return nChannelNumber; }
2019 void SetChannelNumber(int ChannelNumber) const { ((cEventInfo *)this)->nChannelNumber = ChannelNumber; } // doesn't modify the EIT data, so it's ok to make it 'const'
2020 void Dump(FILE *f, const char *Prefix = "") const;
2024 void MVPClient::test(int channelNumber)
2026 FILE* f = fopen("/tmp/test.txt", "w");
2028 cMutexLock MutexLock;
2029 const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
2033 fprintf(f, "Schedules = NULL\n");
2038 fprintf(f, "Schedules dump:\n");
2039 // Schedules->Dump(f);
2041 const cSchedule *Schedule;
2042 cChannel *thisChannel;
2043 const cEventInfo *event;
2045 thisChannel = channelFromNumber(channelNumber);
2048 fprintf(f, "thisChannel = NULL\n");
2053 Schedule = Schedules->GetSchedule(thisChannel->GetChannelID());
2054 // Schedule = Schedules->GetSchedule();
2055 // Schedule = Schedules->First();
2058 fprintf(f, "First Schedule = NULL\n");
2063 fprintf(f, "NumEvents() = %i\n\n", Schedule->NumEvents());
2065 // For some channels VDR seems to pick a random point in time to
2066 // start dishing out events, but they are in order
2067 // at some point in the list the time snaps to the current event
2072 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
2074 event = Schedule->GetEventNumber(eventNumber);
2075 fprintf(f, "Event %i eventid = %u time = %lu duration = %li\n", eventNumber, event->GetEventID(), event->GetTime(), event->GetDuration());
2076 fprintf(f, "Event %i title = %s subtitle = %s\n", eventNumber, event->GetTitle(), event->GetSubtitle());
2077 fprintf(f, "Event %i extendeddescription = %s\n", eventNumber, event->GetExtendedDescription());
2081 fprintf(f, "\nEND\n");
2090 Schedules = the collection of all the Schedule objects
2091 Schedule = One schedule, contants all the events for a channel
2092 Event = One programme
2101 Subtitle (used for "Programmes resume at ...")
2104 IsPresent ? easy to work out tho. Oh it doesn't always work
2107 void MVPClient::test2()
2109 log->log("-", Log::DEBUG, "Timers List");
2111 for (int i = 0; i < Timers.Count(); i++)
2113 cTimer *timer = Timers.Get(i);
2114 //Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, timer->ToText());
2115 log->log("-", Log::DEBUG, "i=%i count=%i index=%d", i, Timers.Count(), timer->Index() + 1);
2116 #if VDRVERSNUM < 10300
2117 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());
2119 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());
2121 log->log("-", Log::DEBUG, "channel=%i file=%s summary=%s", timer->Channel()->Number(), timer->File(), timer->Summary());
2122 log->log("-", Log::DEBUG, "");
2125 // asprintf(&buffer, "%d:%s:%s :%04d:%04d:%d:%d:%s:%s\n",
2126 // active, (UseChannelID ? Channel()->GetChannelID().ToString() : itoa(Channel()->Number())),
2127 // PrintDay(day, firstday), start, stop, priority, lifetime, file, summary ? summary : "");
2131 Active seems to be a bool - whether the timer should be done or not. If set to inactive it stays around after its time
2132 recording is a bool, 0 for not currently recording, 1 for currently recording
2133 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