2 Copyright 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.
23 #ifndef VOMPSTANDALONE
24 #include <vdr/recording.h>
25 #include <vdr/channels.h>
26 #include <vdr/videodir.h>
27 #include <vdr/plugin.h>
28 #include <vdr/timers.h>
30 #include <vdr/remote.h>
31 #include "recplayer.h"
32 #include "mvpreceiver.h"
33 #include "services/scraper2vdr.h"
36 #include "vompclientrrproc.h"
37 #include "vompclient.h"
40 #include "mediaplayer.h"
41 #include "servermediafile.h"
43 #include "vdrcommand.h"
44 #include "picturereader.h"
48 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MIN = 0x00000302;
49 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MAX = 0x00000401;
51 // cc is release protocol version, increase with every release, that changes protocol
52 // dd is development protocol version, set to zero at every release,
53 // increase for every protocol change in git
54 // bb not equal zero should indicate a non loggytronic protocol
55 // aa is reserved for future use
56 // VOMP_PROTOCOL_VERSION_MIN is the protocol version minimal supported by the server
57 // VOMP_PROTOCOL_VERSION_MAX is the protocol version maximal supported by the server
58 // This allows to run older clients from a new server
59 // Increase the minimal protocol version everytime you break compatibility for a certain
63 /* Locking information from VDR:
65 + Instead of directly accessing the global variables Timers, Channels or Recordings,
66 they need to set up a cStateKey variable and call the proper getter function,
69 if (const cTimers *Timers = cTimers::GetTimersRead(StateKey)) {
75 if (cTimers *Timers = cTimers::GetTimersWrite(StateKey)) {
79 See timers.h, thread.h and tools.h for details on this new locking mechanism.
80 + There are convenience macros for easily accessing these lists without having
81 to explicitly set up a cStateKey and calling its Remove() function. These macros
82 have the form LOCK_*_READ/WRITE (with '*' being TIMERS, CHANNELS, SCHEDULES or
83 RECORDINGS). Simply put such a macro before the point where you need to access
84 the respective list, and there will be a pointer named Timers, Channels, Schedules
85 or Recordings, respectively, which is valid until the end of the current block.
86 + If a plugin needs to access several of the global lists in parallel, locking must
87 always be done in the sequence Timers, Channels, Recordings, Schedules. This is
88 necessary to make sure that different threads that need to lock several lists at
89 the same time don't end up in a deadlock.
93 // TODO: Use VDRs recording->ChangeName(option)) for move recording ?
95 ULONG VompClientRRProc::getProtocolVersionMin()
97 return VOMP_PROTOCOL_VERSION_MIN;
100 ULONG VompClientRRProc::getProtocolVersionMax()
102 return VOMP_PROTOCOL_VERSION_MAX;
105 VompClientRRProc::VompClientRRProc(VompClient& x)
108 log = Log::getInstance();
113 VompClientRRProc::~VompClientRRProc()
118 bool VompClientRRProc::init()
120 int a = threadStart();
125 bool VompClientRRProc::recvRequest(RequestPacket* newRequest)
129 Now we have a queue system is used,
130 since on rare occasion the client fire two request at once
131 e.g. heavily channel switching
132 then processing only a single request would cause a deadlock in the client
136 log->log("RRProc", Log::DEBUG, "recvReq");
138 req_queue.push(newRequest);
139 threadSignalNoLock();
140 log->log("RRProc", Log::DEBUG, "recvReq set req and signalled");
146 void VompClientRRProc::threadMethod()
149 log->log("RRProc", Log::DEBUG, "threadMethod startup");
151 if (req_queue.size() != 0)
154 - log->log("RRProc", Log::ERR, "threadMethod err 1");
158 That was how the code used to be.
160 TODO: Work out why this happens.
163 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets");
164 while (req_queue.size())
166 //log->log("RRProc", Log::DEBUG, "thread while");
167 req = req_queue.front();
170 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
172 if (!processPacket())
174 log->log("RRProc", Log::ERR, "processPacket exited with fail");
180 log->log("RRProc", Log::ERR, "threadMethod startup with already queued packets done.");
186 log->log("RRProc", Log::DEBUG, "threadMethod waiting");
187 threadWaitForSignal(); // unlocks, waits, relocks
188 if (req_queue.size() == 0)
190 log->log("RRProc", Log::INFO, "threadMethod err 2 or quit");
195 // signalled with something in queue
197 log->log("RRProc", Log::DEBUG, "thread woken with req, queue size: %i", req_queue.size());
199 while (req_queue.size())
201 //log->log("RRProc", Log::DEBUG, "thread while");
202 req = req_queue.front();
205 threadUnlock(); // allow recvRequest to be queuing packets while we are working on this one
207 if (!processPacket())
209 log->log("RRProc", Log::ERR, "processPacket exited with fail");
216 // locked and run out of packets to process
220 bool VompClientRRProc::processPacket()
222 resp = new ResponsePacket();
223 if (!resp->init(req->requestID))
225 log->log("RRProc", Log::ERR, "response packet init fail");
228 if (req->data) free(req->data);
240 result = processLogin();
242 #ifndef VOMPSTANDALONE
244 result = processGetRecordingsList();
247 result = processDeleteRecording();
250 result = processDeleteRecResume();
253 result = processGetChannelsList();
256 result = processStartStreamingChannel();
259 result = processGetBlock();
262 result = processStopStreaming();
265 result = processStartStreamingRecording();
268 result = processGetChannelSchedule();
272 result = processConfigSave();
275 result = processConfigLoad();
277 #ifndef VOMPSTANDALONE
279 result = processReScanRecording(); // FIXME obselete
282 result = processGetTimers();
285 result = processSetTimer();
288 result = processPositionFromFrameNumber();
291 result = processFrameNumberFromPosition();
294 result = processMoveRecording();
297 result = processGetIFrame();
300 result = processGetRecInfo();
303 result = processGetMarks();
306 result = processGetChannelPids();
309 result = processDeleteTimer();
312 result = processVDRShutdown();
314 case VDR_GETRECSCRAPEREVENTTYPE:
315 result = processGetRecScraperEventType();
317 case VDR_GETSCRAPERMOVIEINFO:
318 result = processGetScraperMovieInfo();
320 case VDR_GETSCRAPERSERIESINFO:
321 result = processGetScraperSeriesInfo();
323 case VDR_LOADTVMEDIA:
324 result = processLoadTvMedia();
326 case VDR_LOADTVMEDIARECTHUMB:
327 result = processLoadTvMediaRecThumb();
329 case VDR_GETEVENTSCRAPEREVENTTYPE:
330 result = processGetEventScraperEventType();
332 case VDR_LOADTVMEDIAEVENTTHUMB:
333 result = processLoadTvMediaEventThumb();
335 case VDR_LOADCHANNELLOGO:
336 result = processLoadChannelLogo();
339 case VDR_GETMEDIALIST:
340 result = processGetMediaList();
343 result = processOpenMedia();
345 case VDR_GETMEDIABLOCK:
346 result = processGetMediaBlock();
349 result = processGetLanguageList();
352 result = processGetLanguageContent();
354 case VDR_GETMEDIAINFO:
355 result = processGetMediaInfo();
357 case VDR_CLOSECHANNEL:
358 result = processCloseMediaChannel();
361 result = processSetCharset();
368 if (req->data) free(req->data);
372 if (result) return true;
377 int VompClientRRProc::processLogin()
379 if (req->dataLength != 6) return 0;
383 char configFileName[PATH_MAX];
384 snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", x.configDir, req->data[0], req->data[1], req->data[2], req->data[3], req->data[4], req->data[5]);
385 x.config.init(configFileName);
387 // Send the login reply
389 time_t timeNow = time(NULL);
390 struct tm* timeStruct = localtime(&timeNow);
391 int timeOffset = timeStruct->tm_gmtoff;
393 resp->addULONG(timeNow);
394 resp->addLONG(timeOffset);
395 resp->addULONG(VOMP_PROTOCOL_VERSION_MIN);
396 resp->addULONG(VOMP_PROTOCOL_VERSION_MAX);
398 // also send information about languages
399 resp->addULONG(I18nLanguages()->Size());
400 resp->addLONG(Setup.DisplaySubtitles);
401 for (int i=0;i < I18nLanguages()->Size(); i++) {
402 resp->addLONG(Setup.AudioLanguages[i]);
403 resp->addLONG(Setup.SubtitleLanguages[i]);
404 resp->addString(I18nLanguageCode(i));
408 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
409 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
412 x.netLog(); // safe to run here since the client won't start net logging for a while yet
417 int VompClientRRProc::processSetCharset()
419 int charset = ntohl(*(ULONG*)req->data);
420 if (charset>0 && charset<3)
422 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
423 x.setCharset(charset);
428 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
432 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
436 int VompClientRRProc::processConfigSave()
438 char* section = (char*)req->data;
442 for (UINT k = 0; k < req->dataLength; k++)
444 if (req->data[k] == '\0')
448 key = (char*)&req->data[k+1];
452 value = (char*)&req->data[k+1];
458 // if the last string (value) doesnt have null terminator, give up
459 if (req->data[req->dataLength - 1] != '\0') return 0;
461 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
462 if (x.config.setValueString(section, key, value))
472 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
477 int VompClientRRProc::processConfigLoad()
479 char* section = (char*)req->data;
482 for (UINT k = 0; k < req->dataLength; k++)
484 if (req->data[k] == '\0')
486 key = (char*)&req->data[k+1];
491 char* value = x.config.getValueString(section, key);
495 resp->addString(value);//client coding, do not touch
496 log->log("RRProc", Log::DEBUG, "Written config load packet");
502 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
506 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
512 //helper for sending from a serialize buffer
513 //insert the used len into the first 4 Bytes of the buffer
514 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
515 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
517 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
521 * media List Request:
522 * Media List response:
525 #define MLISTBUF 500000
526 int VompClientRRProc::processGetMediaList()
528 SerializeBuffer buffer(req->data,req->dataLength);
529 MediaURI uri(0,NULL,NULL);
530 VDR_GetMediaListRequest request(&uri);
531 if (request.deserialize(&buffer) != 0) {
532 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
535 const char *dirname=uri.getName();
536 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
539 if (dirname == NULL) {
540 ml=x.media->getRootList();
542 ml=x.media->getMediaList(&uri);
545 log->log("Client", Log::ERR, "getMediaList returned NULL");
548 SerializeBuffer rbuf(MLISTBUF,false,true);
549 ULONG flags=0; //TODO: real error handling by setting flags
550 VDR_GetMediaListResponse response(&flags,ml);
551 if (response.serialize(&rbuf) != 0) {
552 log->log("Client", Log::ERR, "getMediaList returned NULL");
556 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
561 log->log("Client", Log::DEBUG, "Written Media list");
566 * openMedia response:
568 int VompClientRRProc::processOpenMedia()
570 SerializeBuffer buffer(req->data,req->dataLength);
571 MediaURI uri(0,NULL,NULL);
575 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
576 if (request.deserialize(&buffer) != 0) {
577 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
580 const char *name=uri.getName();
581 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
583 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
588 log->log("Client", Log::ERR, "openMediaRequest unable to open");
590 VDR_OpenMediumResponse response(&flags,&size);
591 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
592 if (response.serialize(&rbuf) != 0) {
593 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
596 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
603 * packet - no serialized response!
605 int VompClientRRProc::processGetMediaBlock()
607 SerializeBuffer buffer(req->data,req->dataLength);
611 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
612 if (request.deserialize(&buffer) != 0) {
613 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
616 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
618 UCHAR sendBuffer[amount ];
619 ULONG amountReceived = 0;
620 UCHAR *rbuf=sendBuffer;
621 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
622 if (!amountReceived || rt != 0)
624 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
628 if (rbuf != sendBuffer) {
629 //the provider did not use the optimized handling with using my buffer
630 resp->copyin(rbuf,amountReceived);
633 // the provider did not allocate a new buffer
634 resp->copyin(sendBuffer,amountReceived);
638 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
639 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
646 int VompClientRRProc::processGetMediaInfo()
648 SerializeBuffer buffer(req->data,req->dataLength);
650 VDR_GetMediaInfoRequest request(&channel);
651 if (request.deserialize(&buffer) != 0) {
652 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
655 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
658 int rt=x.media->getMediaInfo(channel,&mi);
661 log->log("Client", Log::ERR, "getMediaInfo unable to get");
663 VDR_GetMediaInfoResponse response(&flags,&mi);
664 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
665 if (response.serialize(&rbuf) != 0) {
666 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
678 int VompClientRRProc::processCloseMediaChannel()
680 SerializeBuffer buffer(req->data,req->dataLength);
682 VDR_CloseMediaChannelRequest request(&channel);
683 if (request.deserialize(&buffer) != 0) {
684 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
688 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
689 int rt=x.media->closeMediaChannel(channel);
692 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
694 VDR_CloseMediaChannelResponse response(&flags);
695 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
696 if (response.serialize(&rbuf) != 0) {
697 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
706 int VompClientRRProc::processGetLanguageList()
708 x.i18n.findLanguages();
709 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
711 I18n::lang_code_list::const_iterator iter;
712 for (iter = languages.begin(); iter != languages.end(); ++iter)
714 resp->addString(iter->first.c_str()); // Source code is acsii
715 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
718 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
722 int VompClientRRProc::processGetLanguageContent()
724 if (req->dataLength <= 0) return 0;
725 std::string code, result;
726 code.assign((char*)req->data, req->dataLength - 1);
727 x.i18n.findLanguages();
728 I18n::trans_table texts = x.i18n.getLanguageContent(code);
729 I18n::trans_table::const_iterator iter;
730 for (iter = texts.begin(); iter != texts.end(); ++iter)
732 resp->addString(iter->first.c_str());// source code is acsii since it is english
733 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
736 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
740 #ifndef VOMPSTANDALONE
742 int VompClientRRProc::processGetRecordingsList()
745 #if APIVERSNUM > 20101
746 int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB);
748 int Percent = VideoDiskSpace(&FreeMB);
750 int Total = (FreeMB / (100 - Percent)) * 100;
752 resp->addULONG(Total);
753 resp->addULONG(FreeMB);
754 resp->addULONG(Percent);
756 #if VDRVERSNUM >= 20301
757 LOCK_RECORDINGS_READ;
758 const cRecordings* tRecordings = Recordings;
760 cThreadLock RecordingsLock(&Recordings);
761 const cRecordings* tRecordings = &Recordings;
764 for (const cRecording *recording = tRecordings->First(); recording; recording = tRecordings->Next(recording))
766 #if VDRVERSNUM < 10721
767 resp->addULONG(recording->start);
769 resp->addULONG(recording->Start());
771 resp->addUCHAR(recording->IsNew() ? 1 : 0);
772 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
773 resp->addString(recording->FileName());//file name are not visible by user do not touch
777 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
779 log->log("RRProc", Log::DEBUG, "Written recordings list");
784 int VompClientRRProc::processDeleteRecording()
786 // data is a pointer to the fileName string
788 #if VDRVERSNUM >= 20301
789 LOCK_RECORDINGS_WRITE;
790 cRecordings* tRecordings = Recordings;
792 cThreadLock RecordingsLock(&Recordings);
793 cRecordings* tRecordings = &Recordings;
796 cRecording* recording = tRecordings->GetByName((char*)req->data);
798 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
802 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
804 // TODO: Switch to using: cRecording::IsInUse(void) const
805 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
808 if (recording->Delete())
810 #if VDRVERSNUM >= 20301
811 tRecordings->DelByName(recording->FileName());
812 tRecordings->SetModified();
813 #elif VDRVERSNUM > 10300
814 // Copy svdrp's way of doing this, see if it works
815 ::Recordings.DelByName(recording->FileName());
835 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
840 int VompClientRRProc::processDeleteRecResume()
842 // data is a pointer to the fileName string
844 #if VDRVERSNUM < 20301
845 resp->addULONG(5); // Not supported
847 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
852 const cRecordings* Recordings = cRecordings::GetRecordingsRead(StateKey);
853 const cRecording* recording = Recordings->GetByName((char*)req->data);
857 log->log("RRProc", Log::DEBUG, "deleting recording resume : %s", recording->Name());
859 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
862 if (ResumeFile.Read() >= 0)
865 resp->addULONG(1); // success
869 resp->addULONG(2); // failed, no resume point saved
875 resp->addULONG(4); // failed to find recording
879 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
884 int VompClientRRProc::processMoveRecording()
886 log->log("RRProc", Log::DEBUG, "Process move recording");
887 char* fileName = (char*)req->data;
888 char* newPath = NULL;
890 for (UINT k = 0; k < req->dataLength; k++)
892 if (req->data[k] == '\0')
894 newPath = (char*)&req->data[k+1];
898 if (!newPath) return 0;
901 #if VDRVERSNUM >= 20301
902 LOCK_RECORDINGS_WRITE;
903 cRecordings* tRecordings = Recordings;
905 cThreadLock RecordingsLock(&Recordings);
906 cRecordings* tRecordings = &Recordings;
909 cRecording* recording = tRecordings->GetByName((char*)fileName);
911 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
915 // TODO: Switch to using: int cRecording::IsInUse(void) const
916 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
919 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
920 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
921 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
923 const char* t = recording->FileName();
925 char* dateDirName = NULL; int k;
926 char* titleDirName = NULL; int j;
928 // Find the datedirname
929 for(k = strlen(t) - 1; k >= 0; k--)
933 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
934 dateDirName = new char[strlen(&t[k+1]) + 1];
935 strcpy(dateDirName, &t[k+1]);
940 // Find the titledirname
942 for(j = k-1; j >= 0; j--)
946 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
947 titleDirName = new char[(k - j - 1) + 1];
948 memcpy(titleDirName, &t[j+1], k - j - 1);
949 titleDirName[k - j - 1] = '\0';
954 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
955 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
956 #if APIVERSNUM > 20101
957 log->log("RRProc", Log::DEBUG, "viddir: %s", cVideoDirectory::Name());
959 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
962 char* newPathConv = (char*)malloc(strlen(newPath)+1);
963 strcpy(newPathConv, newPath);
964 newPathConv = ExchangeChars(newPathConv, true);
965 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
967 #if APIVERSNUM > 20101
968 char* newContainer = new char[strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1];
969 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1);
970 sprintf(newContainer, "%s%s%s", cVideoDirectory::Name(), newPathConv, titleDirName);
972 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
973 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
974 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
978 log->log("RRProc", Log::DEBUG, "%s", newContainer);
981 int statret = stat(newContainer, &dstat);
982 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
984 log->log("RRProc", Log::DEBUG, "new dir does not exist");
985 int mkdirret = mkdir(newContainer, 0755);
988 delete[] dateDirName;
989 delete[] titleDirName;
990 delete[] newContainer;
994 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
998 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
1000 delete[] dateDirName;
1001 delete[] titleDirName;
1002 delete[] newContainer;
1006 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1011 // Ok, the directory container has been made, or it pre-existed.
1013 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
1014 sprintf(newDir, "%s/%s", newContainer, dateDirName);
1016 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
1017 int renameret = rename(t, newDir);
1020 // Success. Test for remove old dir containter
1021 char* oldTitleDir = new char[k+1];
1022 memcpy(oldTitleDir, t, k);
1023 oldTitleDir[k] = '\0';
1024 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
1025 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
1026 delete[] oldTitleDir;
1028 #if VDRVERSNUM >= 20301
1029 tRecordings->SetModified();
1030 #elif VDRVERSNUM > 10311
1031 ::Recordings.Update();
1033 // Success. Send a different packet from just a ulong
1034 resp->addULONG(1); // success
1035 resp->addString(newDir); //system depent do not convert
1043 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1045 delete[] dateDirName;
1046 delete[] titleDirName;
1047 delete[] newContainer;
1054 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1061 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1067 int VompClientRRProc::processGetChannelsList()
1071 char* chanConfig = x.config.getValueString("General", "Channels");
1073 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
1075 #if VDRVERSNUM >= 20301
1077 const cChannels* tChannels = Channels;
1079 const cChannels* tChannels = &Channels;
1082 for (const cChannel *channel = tChannels->First(); channel; channel = tChannels->Next(channel))
1084 #if VDRVERSNUM < 10300
1085 if (!channel->GroupSep() && (!channel->Ca() || allChans))
1087 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
1090 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
1092 if (channel->Vpid()) type = 1;
1093 #if VDRVERSNUM < 10300
1096 else if (channel->Apid(0)) type = 2;
1100 resp->addULONG(channel->Number());
1101 resp->addULONG(type);
1102 resp->addString(x.charconvsys->Convert(channel->Name()));
1103 #if VDRVERSNUM < 10703
1106 resp->addULONG(channel->Vtype());
1112 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1114 log->log("RRProc", Log::DEBUG, "Written channels list");
1119 int VompClientRRProc::processGetChannelPids()
1121 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1123 #if VDRVERSNUM >= 20301
1125 const cChannels* tChannels = Channels;
1127 cChannels* tChannels = &Channels;
1130 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1135 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1144 #if VDRVERSNUM < 10300
1146 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
1147 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
1149 if (channel->Apid2())
1151 else if (channel->Apid1())
1158 for (const int *Apid = channel->Apids(); *Apid; Apid++)
1162 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
1166 for (const int *Spid = channel->Spids(); *Spid; Spid++)
1173 // Format of response
1192 resp->addULONG(channel->Vpid());
1193 #if VDRVERSNUM < 10703
1196 resp->addULONG(channel->Vtype());
1198 resp->addULONG(numApids);
1200 #if VDRVERSNUM < 10300
1203 resp->addULONG(channel->Apid1());
1204 resp->addString("");
1208 resp->addULONG(channel->Apid2());
1209 resp->addString("");
1214 for (ULONG i = 0; i < numApids; i++)
1216 resp->addULONG(channel->Apid(i));
1217 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1219 resp->addULONG(numDpids);
1220 for (ULONG i = 0; i < numDpids; i++)
1222 resp->addULONG(channel->Dpid(i));
1223 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1225 resp->addULONG(numSpids);
1226 for (ULONG i = 0; i < numSpids; i++)
1228 resp->addULONG(channel->Spid(i));
1229 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1232 resp->addULONG(channel->Tpid());
1233 // Format of extended response, for compatibility with older client at the end
1245 #if VDRVERSNUM < 10300
1255 for (ULONG i = 0; i < numApids; i++)
1257 #if VDRVERSNUM < 10715
1260 resp->addULONG(channel->Atype(i));
1263 for (ULONG i = 0; i < numDpids; i++)
1265 #if VDRVERSNUM < 10715
1266 resp->addULONG(0x6A /*AC3*/);
1268 resp->addULONG(channel->Dtype(i));
1271 for (ULONG i = 0; i < numSpids; i++)
1273 #if VDRVERSNUM < 10715
1278 resp->addULONG(channel->SubtitlingType(i));
1279 resp->addULONG(channel->CompositionPageId(i));
1280 resp->addULONG(channel->AncillaryPageId(i));
1287 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1289 log->log("RRProc", Log::DEBUG, "Written channels pids");
1294 int VompClientRRProc::processStartStreamingChannel()
1298 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1302 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1303 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1305 #if VDRVERSNUM >= 20301
1307 const cChannels* tChannels = Channels;
1309 cChannels* tChannels = &Channels;
1312 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1317 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1321 // get the priority we should use
1323 int priority = x.config.getValueLong("General", "Live priority", &fail);
1326 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1330 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1334 // a bit of sanity..
1335 #if VDRVERSNUM < 10725
1336 if (priority < 0) priority = 0;
1338 if (priority < -99) priority = -99;
1340 if (priority > 99) priority = 99;
1342 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1343 x.lp = MVPReceiver::create(channel, priority);
1349 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1353 if (!x.lp->init(&x.tcp, req->requestID))
1359 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1365 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1369 int VompClientRRProc::processStopStreaming()
1371 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1374 x.lp->detachMVPReceiver();
1378 else if (x.recplayer)
1380 x.writeResumeData();
1388 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1392 int VompClientRRProc::processGetBlock()
1396 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1402 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1406 UCHAR* data = req->data;
1408 ULLONG position = x.ntohll(*(ULLONG*)data);
1409 data += sizeof(ULLONG);
1410 ULONG amount = ntohl(*(ULONG*)data);
1412 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1414 UCHAR sendBuffer[amount];
1415 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1417 if (!amountReceived)
1420 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1424 resp->copyin(sendBuffer, amountReceived);
1425 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1429 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1430 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1434 int VompClientRRProc::processStartStreamingRecording()
1436 // data is a pointer to the fileName string
1438 #if VDRVERSNUM >= 20301
1439 LOCK_RECORDINGS_READ;
1440 const cRecordings* tRecordings = Recordings;
1442 cThreadLock RecordingsLock(&Recordings);
1443 cRecordings* tRecordings = &Recordings;
1446 const cRecording* recording = tRecordings->GetByName((char*)req->data);
1448 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1452 x.recplayer = new RecPlayer(recording);
1454 resp->addULLONG(x.recplayer->getLengthBytes());
1455 resp->addULONG(x.recplayer->getLengthFrames());
1457 #if VDRVERSNUM < 10703
1458 resp->addUCHAR(true);//added for TS
1460 resp->addUCHAR(recording->IsPesRecording());//added for TS
1464 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1466 log->log("RRProc", Log::DEBUG, "written totalLength");
1472 int VompClientRRProc::processPositionFromFrameNumber()
1476 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1480 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1484 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1487 resp->addULLONG(retval);
1489 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1491 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1495 int VompClientRRProc::processFrameNumberFromPosition()
1499 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1503 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1507 retval = x.recplayer->frameNumberFromPosition(position);
1510 resp->addULONG(retval);
1512 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1514 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1518 int VompClientRRProc::processGetIFrame()
1520 bool success = false;
1522 ULONG* data = (ULONG*)req->data;
1524 ULONG frameNumber = ntohl(*data);
1526 ULONG direction = ntohl(*data);
1528 ULLONG rfilePosition = 0;
1529 ULONG rframeNumber = 0;
1530 ULONG rframeLength = 0;
1534 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1538 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1541 // returns file position, frame number, length
1545 resp->addULLONG(rfilePosition);
1546 resp->addULONG(rframeNumber);
1547 resp->addULONG(rframeLength);
1555 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1557 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1561 int VompClientRRProc::processGetChannelSchedule()
1563 ULONG* data = (ULONG*)req->data;
1565 ULONG channelNumber = ntohl(*data);
1567 ULONG startTime = ntohl(*data);
1569 ULONG duration = ntohl(*data);
1571 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1573 #if VDRVERSNUM >= 20301
1575 const cChannels* tChannels = Channels;
1577 cChannels* tChannels = &Channels;
1580 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1585 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1587 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1591 log->log("RRProc", Log::DEBUG, "Got channel");
1593 #if VDRVERSNUM < 10300
1594 cMutexLock MutexLock;
1595 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
1596 #elif VDRVERSNUM < 20301
1597 cSchedulesLock MutexLock;
1598 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
1600 LOCK_SCHEDULES_READ;
1601 const cSchedules *tSchedules = Schedules;
1608 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1610 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1614 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1616 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
1621 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1623 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1627 log->log("RRProc", Log::DEBUG, "Got schedule object");
1629 const char* empty = "";
1630 bool atLeastOneEvent = false;
1633 ULONG thisEventTime;
1634 ULONG thisEventDuration;
1635 const char* thisEventTitle;
1636 const char* thisEventSubTitle;
1637 const char* thisEventDescription;
1639 #if VDRVERSNUM < 10300
1641 const cEventInfo *event;
1642 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1644 event = Schedule->GetEventNumber(eventNumber);
1646 thisEventID = event->GetEventID();
1647 thisEventTime = event->GetTime();
1648 thisEventDuration = event->GetDuration();
1649 thisEventTitle = event->GetTitle();
1650 thisEventSubTitle = event->GetSubtitle();
1651 thisEventDescription = event->GetExtendedDescription();
1655 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1657 thisEventID = event->EventID();
1658 thisEventTime = event->StartTime();
1659 thisEventDuration = event->Duration();
1660 thisEventTitle = event->Title();
1661 thisEventSubTitle = NULL;
1662 thisEventDescription = event->Description();
1666 //in the past filter
1667 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1670 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1673 if (thisEventTime >= (startTime + duration)) continue;
1675 if (!thisEventTitle) thisEventTitle = empty;
1676 if (!thisEventSubTitle) thisEventSubTitle = empty;
1677 if (!thisEventDescription) thisEventDescription = empty;
1679 resp->addULONG(thisEventID);
1680 resp->addULONG(thisEventTime);
1681 resp->addULONG(thisEventDuration);
1683 resp->addString(x.charconvsys->Convert(thisEventTitle));
1684 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1685 resp->addString(x.charconvsys->Convert(thisEventDescription));
1687 atLeastOneEvent = true;
1690 log->log("RRProc", Log::DEBUG, "Got all event data");
1692 if (!atLeastOneEvent)
1695 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1699 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1701 log->log("RRProc", Log::DEBUG, "written schedules packet");
1706 int VompClientRRProc::processGetTimers()
1708 #if VDRVERSNUM >= 20301
1710 const cTimers* tTimers = Timers;
1712 const cTimers* tTimers = &Timers;
1715 const cTimer *timer;
1716 int numTimers = tTimers->Count();
1718 resp->addULONG(numTimers);
1720 for (int i = 0; i < numTimers; i++)
1722 timer = tTimers->Get(i);
1724 #if VDRVERSNUM < 10300
1725 resp->addULONG(timer->Active());
1727 resp->addULONG(timer->HasFlags(tfActive));
1729 resp->addULONG(timer->Recording());
1730 resp->addULONG(timer->Pending());
1731 resp->addULONG(timer->Priority());
1732 resp->addULONG(timer->Lifetime());
1733 resp->addULONG(timer->Channel()->Number());
1734 resp->addULONG(timer->StartTime());
1735 resp->addULONG(timer->StopTime());
1736 resp->addULONG(timer->Day());
1737 resp->addULONG(timer->WeekDays());
1738 resp->addString(timer->File()); //Filename is system specific and not visible by user
1742 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1744 log->log("RRProc", Log::DEBUG, "Written timers list");
1749 int VompClientRRProc::processSetTimer()
1751 char* timerString = new char[strlen((char*)req->data) + 1];
1752 strcpy(timerString, (char*)req->data);
1754 #if VDRVERSNUM < 10300
1756 // If this is VDR 1.2 the date part of the timer string must be reduced
1757 // to just DD rather than YYYY-MM-DD
1759 int s = 0; // source
1760 int d = 0; // destination
1762 while(c != 2) // copy up to date section, including the second ':'
1764 timerString[d] = req->data[s];
1765 if (req->data[s] == ':') c++;
1769 // now it has copied up to the date section
1771 while(c != 2) // waste YYYY-MM-
1773 if (req->data[s] == '-') c++;
1776 // now source is at the DD
1777 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1778 d += req->dataLength - s;
1779 timerString[d] = '\0';
1781 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1784 log->log("RRProc", Log::DEBUG, "%s", timerString);
1786 cTimer *timer = new cTimer;
1787 if (!timer->Parse((char*)timerString))
1791 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1796 #if VDRVERSNUM >= 20301
1798 cTimers* tTimers = Timers;
1800 cTimers* tTimers = &Timers;
1803 cTimer *t = tTimers->GetTimer(timer);
1808 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1813 timer->ClrFlags(tfRecording);
1814 tTimers->Add(timer);
1815 #if VDRVERSNUM < 10300
1817 #elif VDRVERSNUM < 20301
1818 tTimers->SetModified();
1823 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1827 int VompClientRRProc::processDeleteTimer()
1829 log->log("RRProc", Log::DEBUG, "Delete timer called");
1834 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1835 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1836 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1837 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1838 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1840 #if VDRVERSNUM >= 20301
1842 cTimers* tTimers = Timers;
1844 cTimers* tTimers = &Timers;
1848 for (ti = tTimers->First(); ti; ti = tTimers->Next(ti))
1850 if ( (ti->Channel()->Number() == delChannel)
1851 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1852 && (ti->StartTime() == delStart)
1853 && (ti->StopTime() == delStop) )
1861 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1865 #if VDRVERSNUM < 20301
1866 // I suppose with the new locking this just can't happen
1867 if (tTimers->BeingEdited())
1869 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1872 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1877 if (ti->Recording())
1879 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1882 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1887 tTimers->SetModified();
1891 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1895 int VompClientRRProc::processGetRecInfo()
1897 // data is a pointer to the fileName string
1898 #if VDRVERSNUM >= 20301
1899 LOCK_RECORDINGS_READ;
1900 const cRecordings* tRecordings = Recordings;
1902 cThreadLock RecordingsLock(&Recordings);
1903 cRecordings* tRecordings = &Recordings;
1906 const cRecording *recording = tRecordings->GetByName((char*)req->data);
1908 time_t timerStart = 0;
1909 time_t timerStop = 0;
1910 char* summary = NULL;
1911 char* shorttext = NULL;
1912 char* description = NULL;
1914 bool newsummary=false;
1915 ULONG resumePoint = 0;
1919 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1922 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1927 4 bytes: start time for timer
1928 4 bytes: end time for timer
1929 4 bytes: resume point
1931 4 bytes: num components
1938 8 bytes: frames per second
1941 // Get current timer
1943 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1946 timerStart = rc->Timer()->StartTime();
1947 timerStop = rc->Timer()->StopTime();
1948 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1951 resp->addULONG(timerStart);
1952 resp->addULONG(timerStop);
1956 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1959 resumePoint = strtoul(value, NULL, 10);
1963 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1966 ResumeId = atoi(ResumeIdC);
1970 ResumeId = 0; //default if not defined in vomp-MAC.conf
1972 while (ResumeIDLock)
1973 cCondWait::SleepMs(100);
1974 ResumeIDLock = true;
1975 int OldSetupResumeID = Setup.ResumeID;
1976 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1977 #if VDRVERSNUM < 10703
1978 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1980 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
1982 Setup.ResumeID = OldSetupResumeID; //and restore it back
1983 ResumeIDLock = false;
1985 int resume = ResumeFile.Read();
1986 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
1988 resumePoint = ResumeFile.Read();
1990 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
1992 resp->addULONG(resumePoint);
1996 #if VDRVERSNUM < 10300
1997 summary = (char*)recording->Summary();
1999 const cRecordingInfo *Info = recording->Info();
2000 shorttext = (char*)Info->ShortText();
2001 description = (char*) (char*)Info->Description();
2002 if (isempty(shorttext)) summary=description;
2003 else if (isempty(description)) summary=shorttext;
2005 int length=strlen(description)+strlen(shorttext)+4;
2006 summary=new char[length];
2007 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2011 if (isempty(summary)) summary = (char*)Info->Description();
2013 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2016 resp->addString(x.charconvsys->Convert(summary));
2017 if (newsummary) delete [] summary;
2021 resp->addString("");
2026 #if VDRVERSNUM < 10300
2028 // Send 0 for numchannels - this signals the client this info is not available
2032 const cComponents* components = Info->Components();
2034 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2042 resp->addULONG(components->NumComponents());
2044 tComponent* component;
2045 for (int i = 0; i < components->NumComponents(); i++)
2047 component = components->Component(i);
2049 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2051 resp->addUCHAR(component->stream);
2052 resp->addUCHAR(component->type);
2054 if (component->language)
2056 resp->addString(x.charconvsys->Convert(component->language));
2060 resp->addString("");
2062 if (component->description)
2064 resp->addString(x.charconvsys->Convert(component->description));
2068 resp->addString("");
2074 double framespersec;
2075 #if VDRVERSNUM < 10703
2076 framespersec = FRAMESPERSEC;
2078 framespersec = Info->FramesPerSecond();
2080 resp->adddouble(framespersec);
2081 title = (char*)Info->Title();
2084 resp->addString(x.charconvsys->Convert(title));
2088 resp->addString(x.charconvsys->Convert(recording->Name()));
2094 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2096 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2106 int VompClientRRProc::processReScanRecording()
2110 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
2114 x.recplayer->scan();
2116 resp->addULLONG(x.recplayer->getLengthBytes());
2117 resp->addULONG(x.recplayer->getLengthFrames());
2119 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2120 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
2124 // FIXME without client calling rescan, getblock wont work even tho more data is avail
2126 int VompClientRRProc::processGetMarks()
2128 // data is a pointer to the fileName string
2129 #if VDRVERSNUM >= 20301
2130 LOCK_RECORDINGS_READ;
2131 const cRecordings* tRecordings = Recordings;
2133 cThreadLock RecordingsLock(&Recordings);
2134 cRecordings* tRecordings = &Recordings;
2137 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2138 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
2143 #if VDRVERSNUM < 10703
2144 Marks.Load(recording->FileName());
2146 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
2150 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
2152 #if VDRVERSNUM < 10721
2153 ULLONG mposition = m->position;
2155 ULLONG mposition = m->Position();
2157 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
2159 resp->addULONG(mposition);
2164 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
2170 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2172 log->log("RRProc", Log::DEBUG, "Written Marks list");
2177 int VompClientRRProc::processVDRShutdown()
2179 log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
2180 VompClient::decClients(); // Temporarily make this client disappear
2181 cRemote::Put(kPower);
2182 VompClient::incClients();
2184 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2188 int VompClientRRProc::processGetRecScraperEventType()
2190 #if VDRVERSNUM >= 20301
2191 LOCK_RECORDINGS_READ;
2192 const cRecordings* tRecordings = Recordings;
2194 cThreadLock RecordingsLock(&Recordings);
2195 cRecordings* tRecordings = &Recordings;
2198 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2199 ScraperGetEventType call;
2202 if (recording && x.scrapQuery())
2204 call.recording = recording;
2205 x.scraper->Service("GetEventType", &call);
2207 resp->addUCHAR(call.type);
2208 if (call.type == tMovie)
2210 resp->addLONG(call.movieId);
2212 else if (call.type == tSeries)
2214 resp->addLONG(call.seriesId);
2215 resp->addLONG(call.episodeId);
2218 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2223 int VompClientRRProc::processGetEventScraperEventType()
2225 ScraperGetEventType call;
2227 ULONG channelid = ntohl(*(ULONG*)req->data);
2228 ULONG eventid = ntohl(*(ULONG*)(req->data+4));
2229 const cEvent *event = NULL;
2231 #if VDRVERSNUM >= 20301
2233 const cChannels* tChannels = Channels;
2235 cChannels* tChannels = &Channels;
2238 const cChannel* channel = tChannels->GetByNumber(channelid);
2240 #if VDRVERSNUM < 10300
2241 cMutexLock MutexLock;
2242 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
2243 #elif VDRVERSNUM < 20301
2244 cSchedulesLock MutexLock;
2245 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
2247 LOCK_SCHEDULES_READ;
2248 const cSchedules *tSchedules = Schedules;
2251 if (tSchedules && channel)
2253 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
2255 event = Schedule->GetEvent(eventid);
2259 if (event && x.scrapQuery())
2262 x.scraper->Service("GetEventType",&call);
2264 resp->addUCHAR(call.type);
2265 if (call.type == tMovie)
2267 resp->addLONG(call.movieId);
2268 } else if (call.type == tSeries){
2269 resp->addLONG(call.seriesId);
2270 resp->addLONG(call.episodeId);
2272 if (x.pict->epgImageExists(eventid)) {
2279 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2284 #define ADDSTRING_TO_PAKET(y) if ((y)!=0) resp->addString(x.charconvutf8->Convert(y)); else resp->addString("");
2286 int VompClientRRProc::processGetScraperMovieInfo()
2290 movie.movieId = ntohl(*(ULONG*)req->data);
2291 if (!x.scrapQuery()) {
2292 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2293 return 0; //stupid, I have no scraper why are you still asking
2295 x.scraper->Service("GetMovie",&movie);
2298 ADDSTRING_TO_PAKET(movie.title.c_str());
2299 ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
2300 ADDSTRING_TO_PAKET(movie.tagline.c_str());
2301 ADDSTRING_TO_PAKET(movie.overview.c_str());
2302 resp->addUCHAR(movie.adult);
2303 ADDSTRING_TO_PAKET(movie.collectionName.c_str());
2305 resp->addLONG(movie.budget);
2306 resp->addLONG(movie.revenue);
2307 ADDSTRING_TO_PAKET(movie.genres.c_str());
2308 ADDSTRING_TO_PAKET(movie.homepage.c_str());
2309 ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
2310 resp->addLONG(movie.runtime);
2311 resp->adddouble(movie.popularity);
2312 resp->adddouble(movie.voteAverage);
2313 resp->addULONG(movie.poster.width);
2314 resp->addULONG(movie.poster.height);
2315 resp->addULONG(movie.fanart.width);
2316 resp->addULONG(movie.fanart.height);
2317 resp->addULONG(movie.collectionPoster.width);
2318 resp->addULONG(movie.collectionPoster.height);
2319 resp->addULONG(movie.collectionFanart.width);
2320 resp->addULONG(movie.collectionFanart.height);
2321 resp->addULONG(movie.actors.size());
2322 for (ULONG acty=0; acty < movie.actors.size(); acty++) {
2323 ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
2324 ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
2325 resp->addULONG(movie.actors[acty].actorThumb.width);
2326 resp->addULONG(movie.actors[acty].actorThumb.height);
2330 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2337 int VompClientRRProc::processGetScraperSeriesInfo()
2340 series.seriesId = ntohl(*(ULONG*)req->data);
2341 series.episodeId = ntohl(*(ULONG*)(req->data+4));
2342 if (!x.scrapQuery()) {
2343 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2344 return 0; //stupid, I have no scraper why are you still asking
2346 x.scraper->Service("GetSeries",&series);
2348 ADDSTRING_TO_PAKET(series.name.c_str());
2349 ADDSTRING_TO_PAKET(series.overview.c_str());
2350 ADDSTRING_TO_PAKET(series.firstAired.c_str());
2351 ADDSTRING_TO_PAKET(series.network.c_str());
2352 ADDSTRING_TO_PAKET(series.genre.c_str());
2353 resp->adddouble(series.rating);
2354 ADDSTRING_TO_PAKET(series.status.c_str());
2356 resp->addLONG(series.episode.number);
2357 resp->addLONG(series.episode.season);
2358 ADDSTRING_TO_PAKET(series.episode.name.c_str());
2359 ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
2360 ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
2361 ADDSTRING_TO_PAKET(series.episode.overview.c_str());
2362 resp->adddouble(series.episode.rating);
2363 resp->addULONG(series.episode.episodeImage.width);
2364 resp->addULONG(series.episode.episodeImage.height);
2366 ULONG num_actors = series.actors.size();
2367 resp->addULONG(num_actors);
2368 for (ULONG acty=0; acty < num_actors; acty++) {
2369 ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
2370 ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
2371 resp->addULONG(series.actors[acty].actorThumb.width);
2372 resp->addULONG(series.actors[acty].actorThumb.height);
2374 ULONG num_posters = series.posters.size();
2375 resp->addULONG(num_posters);
2376 for (ULONG medias = 0; medias < num_posters; medias++ ) {
2377 cTvMedia media=series.posters[medias];
2378 resp->addULONG(media.width);
2379 resp->addULONG(media.height);
2382 ULONG num_banners = series.banners.size();
2383 resp->addULONG(num_banners);
2384 for (ULONG medias = 0; medias < num_banners; medias++ ) {
2385 cTvMedia media=series.banners[medias];
2386 resp->addULONG(media.width);
2387 resp->addULONG(media.height);
2389 ULONG num_fanarts = series.fanarts.size();
2390 resp->addULONG(num_fanarts);
2391 for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
2392 cTvMedia media=series.fanarts[medias];
2393 resp->addULONG(media.width);
2394 resp->addULONG(media.height);
2396 resp->addULONG(series.seasonPoster.width);
2397 resp->addULONG(series.seasonPoster.height);
2401 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2406 int VompClientRRProc::processLoadTvMedia()
2408 TVMediaRequest tvreq;
2409 tvreq.streamID = req->requestID;
2410 tvreq.type = ntohl(*(ULONG*)req->data);
2411 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2412 tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
2413 tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
2414 tvreq.container = ntohl(*(ULONG*)(req->data+16));
2415 tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
2416 log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
2417 x.pict->addTVMediaRequest(tvreq);
2422 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2427 int VompClientRRProc::processLoadTvMediaRecThumb()
2429 TVMediaRequest tvreq;
2430 tvreq.streamID = req->requestID;
2431 tvreq.type = 3; // unknown but primary_name is set
2432 tvreq.primary_id = 0;
2433 tvreq.primary_name = std::string((const char*) req->data);
2434 tvreq.secondary_id = 0;
2435 tvreq.type_pict = 1;
2436 tvreq.container = 0;
2437 tvreq.container_member = 0;
2438 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2439 x.pict->addTVMediaRequest(tvreq);
2444 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2449 int VompClientRRProc::processLoadTvMediaEventThumb()
2451 TVMediaRequest tvreq;
2452 tvreq.streamID = req->requestID;
2453 tvreq.type = 4; // unknown but primary_id is set
2454 UINT channelid = ntohl(*(ULONG*)req->data);
2455 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2456 tvreq.secondary_id = 0;
2458 #if VDRVERSNUM >= 20301
2460 const cChannels* tChannels = Channels;
2462 cChannels* tChannels = &Channels;
2465 const cChannel* channel = tChannels->GetByNumber(channelid);
2467 if (channel) tvreq.primary_name = std::string((const char*)channel->GetChannelID().ToString());
2468 tvreq.type_pict = 1;
2469 tvreq.container = 0;
2470 tvreq.container_member = 0;
2471 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2472 x.pict->addTVMediaRequest(tvreq);
2477 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2482 int VompClientRRProc::processLoadChannelLogo()
2484 TVMediaRequest tvreq;
2485 tvreq.streamID = req->requestID;
2486 tvreq.type = 5; // channel logo
2487 UINT channelid = ntohl(*(ULONG*)req->data);
2488 tvreq.primary_id = channelid;
2489 tvreq.secondary_id = 0;
2491 #if VDRVERSNUM >= 20301
2493 const cChannels* tChannels = Channels;
2495 cChannels* tChannels = &Channels;
2498 const cChannel* channel = tChannels->GetByNumber(channelid);
2500 if (channel) tvreq.primary_name = std::string((const char*)channel->Name());
2501 tvreq.type_pict = 1;
2502 tvreq.container = 0;
2503 tvreq.container_member = 0;
2504 if (channel) log->log("RRProc", Log::DEBUG, "TVMedia request %d %d %s",req->requestID,channelid, channel->Name());
2505 x.pict->addTVMediaRequest(tvreq);
2510 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2518 #endif // !VOMPSTANDALONE