2 Copyright 2019 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 = 0x00000301;
49 ULONG VompClientRRProc::VOMP_PROTOCOL_VERSION_MAX = 0x00000500;
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);
238 { // FIXME use defined names in vdrcommand.h for these cases
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 = processGetRecInfo2();
306 result = processGetMarks();
309 result = processGetChannelPids();
312 result = processDeleteTimer();
315 result = processVDRShutdown();
317 case VDR_GETRECSCRAPEREVENTTYPE:
318 result = processGetRecScraperEventType();
320 case VDR_GETSCRAPERMOVIEINFO:
321 result = processGetScraperMovieInfo();
323 case VDR_GETSCRAPERSERIESINFO:
324 result = processGetScraperSeriesInfo();
326 case VDR_LOADTVMEDIA:
327 result = processLoadTvMedia();
329 case VDR_LOADTVMEDIARECTHUMB:
330 result = processLoadTvMediaRecThumb();
332 case VDR_GETEVENTSCRAPEREVENTTYPE:
333 result = processGetEventScraperEventType();
335 case VDR_LOADTVMEDIAEVENTTHUMB:
336 result = processLoadTvMediaEventThumb();
338 case VDR_LOADCHANNELLOGO:
339 result = processLoadChannelLogo();
342 case VDR_GETMEDIALIST:
343 result = processGetMediaList();
346 result = processOpenMedia();
348 case VDR_GETMEDIABLOCK:
349 result = processGetMediaBlock();
352 result = processGetLanguageList();
355 result = processGetLanguageContent();
357 case VDR_GETMEDIAINFO:
358 result = processGetMediaInfo();
360 case VDR_CLOSECHANNEL:
361 result = processCloseMediaChannel();
364 result = processSetCharset();
371 if (req->data) free(req->data);
375 if (result) return true;
380 int VompClientRRProc::processLogin()
382 if (req->dataLength != 6) return 0;
386 char configFileName[PATH_MAX];
387 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]);
388 x.config.init(configFileName);
390 // Send the login reply
392 time_t timeNow = time(NULL);
393 struct tm* timeStruct = localtime(&timeNow);
394 int timeOffset = timeStruct->tm_gmtoff;
396 resp->addULONG(timeNow);
397 resp->addLONG(timeOffset);
398 resp->addULONG(VOMP_PROTOCOL_VERSION_MIN);
399 resp->addULONG(VOMP_PROTOCOL_VERSION_MAX);
401 // also send information about languages
402 resp->addULONG(I18nLanguages()->Size());
403 resp->addLONG(Setup.DisplaySubtitles);
404 for (int i=0;i < I18nLanguages()->Size(); i++) {
405 resp->addLONG(Setup.AudioLanguages[i]);
406 resp->addLONG(Setup.SubtitleLanguages[i]);
407 resp->addString(I18nLanguageCode(i));
411 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
412 log->log("RRProc", Log::DEBUG, "written login reply len %lu", resp->getLen());
415 x.netLog(); // safe to run here since the client won't start net logging for a while yet
420 int VompClientRRProc::processSetCharset()
422 int charset = ntohl(*(ULONG*)req->data);
423 if (charset>0 && charset<3)
425 log->log("RRProc", Log::DEBUG, "Set charset to %d", charset);
426 x.setCharset(charset);
431 log->log("RRProc", Log::DEBUG, "Invalid charset %d", charset);
435 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
439 int VompClientRRProc::processConfigSave()
441 char* section = (char*)req->data;
445 for (UINT k = 0; k < req->dataLength; k++)
447 if (req->data[k] == '\0')
451 key = (char*)&req->data[k+1];
455 value = (char*)&req->data[k+1];
461 // if the last string (value) doesnt have null terminator, give up
462 if (req->data[req->dataLength - 1] != '\0') return 0;
464 log->log("RRProc", Log::DEBUG, "Config save: %s %s %s", section, key, value);
465 if (x.config.setValueString(section, key, value))
475 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
480 int VompClientRRProc::processConfigLoad()
482 char* section = (char*)req->data;
485 for (UINT k = 0; k < req->dataLength; k++)
487 if (req->data[k] == '\0')
489 key = (char*)&req->data[k+1];
494 char* value = x.config.getValueString(section, key);
498 resp->addString(value);//client coding, do not touch
499 log->log("RRProc", Log::DEBUG, "Written config load packet");
505 log->log("RRProc", Log::DEBUG, "Written config load failed packet");
509 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
515 //helper for sending from a serialize buffer
516 //insert the used len into the first 4 Bytes of the buffer
517 void VompClientRRProc::sendPacket(SerializeBuffer *b) {
518 resp->copyin(b->getStart(),b->getCurrent()-b->getStart());
520 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
524 * media List Request:
525 * Media List response:
528 #define MLISTBUF 500000
529 int VompClientRRProc::processGetMediaList()
531 SerializeBuffer buffer(req->data,req->dataLength);
532 MediaURI uri(0,NULL,NULL);
533 VDR_GetMediaListRequest request(&uri);
534 if (request.deserialize(&buffer) != 0) {
535 log->log("Client", Log::ERR, "getMediaList unable to deserialize");
538 const char *dirname=uri.getName();
539 log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);
542 if (dirname == NULL) {
543 ml=x.media->getRootList();
545 ml=x.media->getMediaList(&uri);
548 log->log("Client", Log::ERR, "getMediaList returned NULL");
551 SerializeBuffer rbuf(MLISTBUF,false,true);
552 ULONG flags=0; //TODO: real error handling by setting flags
553 VDR_GetMediaListResponse response(&flags,ml);
554 if (response.serialize(&rbuf) != 0) {
555 log->log("Client", Log::ERR, "getMediaList returned NULL");
559 log->log("Client", Log::DEBUG, "getMediaList size %u", ml->size());
564 log->log("Client", Log::DEBUG, "Written Media list");
569 * openMedia response:
571 int VompClientRRProc::processOpenMedia()
573 SerializeBuffer buffer(req->data,req->dataLength);
574 MediaURI uri(0,NULL,NULL);
578 VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
579 if (request.deserialize(&buffer) != 0) {
580 log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
583 const char *name=uri.getName();
584 log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
586 int rt=x.media->openMedium(channel,&uri,&size,xs,ys);
591 log->log("Client", Log::ERR, "openMediaRequest unable to open");
593 VDR_OpenMediumResponse response(&flags,&size);
594 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
595 if (response.serialize(&rbuf) != 0) {
596 log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
599 log->log("Client", Log::DEBUG, "openMediaRequest size %llu", size);
606 * packet - no serialized response!
608 int VompClientRRProc::processGetMediaBlock()
610 SerializeBuffer buffer(req->data,req->dataLength);
614 VDR_GetMediaBlockRequest request(&channel,&position,&amount);
615 if (request.deserialize(&buffer) != 0) {
616 log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
619 log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);
621 UCHAR sendBuffer[amount ];
622 ULONG amountReceived = 0;
623 UCHAR *rbuf=sendBuffer;
624 int rt=x.media->getMediaBlock(channel,position,amount,&amountReceived,&rbuf);
625 if (!amountReceived || rt != 0)
627 log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
631 if (rbuf != sendBuffer) {
632 //the provider did not use the optimized handling with using my buffer
633 resp->copyin(rbuf,amountReceived);
636 // the provider did not allocate a new buffer
637 resp->copyin(sendBuffer,amountReceived);
641 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
642 log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
649 int VompClientRRProc::processGetMediaInfo()
651 SerializeBuffer buffer(req->data,req->dataLength);
653 VDR_GetMediaInfoRequest request(&channel);
654 if (request.deserialize(&buffer) != 0) {
655 log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
658 log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
661 int rt=x.media->getMediaInfo(channel,&mi);
664 log->log("Client", Log::ERR, "getMediaInfo unable to get");
666 VDR_GetMediaInfoResponse response(&flags,&mi);
667 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
668 if (response.serialize(&rbuf) != 0) {
669 log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
681 int VompClientRRProc::processCloseMediaChannel()
683 SerializeBuffer buffer(req->data,req->dataLength);
685 VDR_CloseMediaChannelRequest request(&channel);
686 if (request.deserialize(&buffer) != 0) {
687 log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
691 log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
692 int rt=x.media->closeMediaChannel(channel);
695 log->log("Client", Log::ERR, "closeMediaChannel unable to get");
697 VDR_CloseMediaChannelResponse response(&flags);
698 SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
699 if (response.serialize(&rbuf) != 0) {
700 log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
709 int VompClientRRProc::processGetLanguageList()
711 x.i18n.findLanguages();
712 const I18n::lang_code_list& languages = x.i18n.getLanguageList();
714 I18n::lang_code_list::const_iterator iter;
715 for (iter = languages.begin(); iter != languages.end(); ++iter)
717 resp->addString(iter->first.c_str()); // Source code is acsii
718 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); //translate string can be any utf-8 character
721 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
725 int VompClientRRProc::processGetLanguageContent()
727 if (req->dataLength <= 0) return 0;
728 std::string code, result;
729 code.assign((char*)req->data, req->dataLength - 1);
730 x.i18n.findLanguages();
731 I18n::trans_table texts = x.i18n.getLanguageContent(code);
732 I18n::trans_table::const_iterator iter;
733 for (iter = texts.begin(); iter != texts.end(); ++iter)
735 resp->addString(iter->first.c_str());// source code is acsii since it is english
736 resp->addString(x.charconvutf8->Convert(iter->second.c_str())); // translate text can be any unicode string, it is stored as UTF-8
739 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
743 #ifndef VOMPSTANDALONE
745 int VompClientRRProc::processGetRecordingsList()
748 #if APIVERSNUM > 20101
749 int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB);
751 int Percent = VideoDiskSpace(&FreeMB);
753 // int Total = (FreeMB / (100 - Percent)) * 100;
754 int Total = 0; // This was never used, which explains why nobody noticed it's total rubbish anyway.
755 // Also it breaks with a lovely divide by 0 crash when your storage gets full.
757 resp->addULONG(Total);
758 resp->addULONG(FreeMB);
759 resp->addULONG(Percent);
761 #if VDRVERSNUM >= 20301
762 LOCK_RECORDINGS_READ;
763 const cRecordings* tRecordings = Recordings;
765 cThreadLock RecordingsLock(&Recordings);
766 const cRecordings* tRecordings = &Recordings;
769 for (const cRecording *recording = tRecordings->First(); recording; recording = tRecordings->Next(recording))
771 #if VDRVERSNUM < 10721
772 resp->addULONG(recording->start);
774 resp->addULONG(recording->Start());
776 resp->addUCHAR(recording->IsNew() ? 1 : 0);
777 resp->addString(x.charconvsys->Convert(recording->Name())); //coding of recording name is system dependent
778 resp->addString(recording->FileName());//file name are not visible by user do not touch
782 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
784 log->log("RRProc", Log::DEBUG, "Written recordings list");
789 int VompClientRRProc::processDeleteRecording()
791 // data is a pointer to the fileName string
793 #if VDRVERSNUM >= 20301
794 LOCK_RECORDINGS_WRITE;
795 cRecordings* tRecordings = Recordings;
797 cThreadLock RecordingsLock(&Recordings);
798 cRecordings* tRecordings = &Recordings;
801 cRecording* recording = tRecordings->GetByName((char*)req->data);
803 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
807 log->log("RRProc", Log::DEBUG, "deleting recording: %s", recording->Name());
809 // TODO: Switch to using: cRecording::IsInUse(void) const
810 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
813 if (recording->Delete())
815 #if VDRVERSNUM >= 20301
816 tRecordings->DelByName(recording->FileName());
817 tRecordings->SetModified();
818 #elif VDRVERSNUM > 10300
819 // Copy svdrp's way of doing this, see if it works
820 ::Recordings.DelByName(recording->FileName());
840 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
845 int VompClientRRProc::processDeleteRecResume()
847 // data is a pointer to the fileName string
849 #if VDRVERSNUM < 20301
850 resp->addULONG(5); // Not supported
852 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
857 const cRecordings* Recordings = cRecordings::GetRecordingsRead(StateKey);
858 const cRecording* recording = Recordings->GetByName((char*)req->data);
862 log->log("RRProc", Log::DEBUG, "deleting recording resume : %s", recording->Name());
864 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
867 if (ResumeFile.Read() >= 0)
870 resp->addULONG(1); // success
874 resp->addULONG(2); // failed, no resume point saved
880 resp->addULONG(4); // failed to find recording
884 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
891 int VompClientRRProc::processMoveRecording()
893 log->log("RRProc", Log::DEBUG, "Process move recording");
894 char* fileName = (char*)req->data;
895 char* newPath = NULL;
897 for (UINT k = 0; k < req->dataLength; k++)
899 if (req->data[k] == '\0')
901 newPath = (char*)&req->data[k+1];
905 if (!newPath) return 0;
908 #if VDRVERSNUM >= 20301
909 LOCK_RECORDINGS_WRITE;
910 cRecordings* tRecordings = Recordings;
912 cThreadLock RecordingsLock(&Recordings);
913 cRecordings* tRecordings = &Recordings;
916 cRecording* recording = tRecordings->GetByName((char*)fileName);
918 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
920 if (recording) // FIXME rewrite these error conditions!
922 // TODO: Switch to using: int cRecording::IsInUse(void) const
923 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
926 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->Name());
927 log->log("RRProc", Log::DEBUG, "moving recording: %s", recording->FileName());
928 log->log("RRProc", Log::DEBUG, "to: %s", newPath);
930 const char* t = recording->FileName();
932 char* dateDirName = NULL; int k;
933 char* titleDirName = NULL; int j;
935 // Find the datedirname
936 for(k = strlen(t) - 1; k >= 0; k--)
940 log->log("RRProc", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
941 dateDirName = new char[strlen(&t[k+1]) + 1];
942 strcpy(dateDirName, &t[k+1]);
947 // Find the titledirname
949 for(j = k-1; j >= 0; j--)
953 log->log("RRProc", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
954 titleDirName = new char[(k - j - 1) + 1];
955 memcpy(titleDirName, &t[j+1], k - j - 1);
956 titleDirName[k - j - 1] = '\0';
961 log->log("RRProc", Log::DEBUG, "datedirname: %s", dateDirName);
962 log->log("RRProc", Log::DEBUG, "titledirname: %s", titleDirName);
963 #if APIVERSNUM > 20101
964 log->log("RRProc", Log::DEBUG, "viddir: %s", cVideoDirectory::Name());
966 log->log("RRProc", Log::DEBUG, "viddir: %s", VideoDirectory);
969 char* newPathConv = (char*)malloc(strlen(newPath)+1);
970 strcpy(newPathConv, newPath);
971 newPathConv = ExchangeChars(newPathConv, true);
972 log->log("RRProc", Log::DEBUG, "EC: %s", newPathConv);
974 #if APIVERSNUM > 20101
975 char* newContainer = new char[strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1];
976 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(cVideoDirectory::Name()) + strlen(newPathConv) + strlen(titleDirName) + 1);
977 sprintf(newContainer, "%s%s%s", cVideoDirectory::Name(), newPathConv, titleDirName);
979 char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
980 log->log("RRProc", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
981 sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
985 log->log("RRProc", Log::DEBUG, "%s", newContainer);
988 int statret = stat(newContainer, &dstat);
989 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
991 log->log("RRProc", Log::DEBUG, "new dir does not exist");
992 int mkdirret = mkdir(newContainer, 0755);
995 delete[] dateDirName;
996 delete[] titleDirName;
997 delete[] newContainer;
1001 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1005 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
1007 delete[] dateDirName;
1008 delete[] titleDirName;
1009 delete[] newContainer;
1013 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1018 // Ok, the directory container has been made, or it pre-existed.
1020 char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
1021 sprintf(newDir, "%s/%s", newContainer, dateDirName);
1023 log->log("RRProc", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
1024 int renameret = rename(t, newDir);
1027 // Success. Test for remove old dir containter
1028 char* oldTitleDir = new char[k+1];
1029 memcpy(oldTitleDir, t, k);
1030 oldTitleDir[k] = '\0';
1031 log->log("RRProc", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
1032 rmdir(oldTitleDir); // can't do anything about a fail result at this point.
1033 delete[] oldTitleDir;
1035 #if VDRVERSNUM >= 20301
1036 tRecordings->Update();
1037 #elif VDRVERSNUM > 10311
1038 ::Recordings.Update();
1040 // Success. Send a different packet from just a ulong
1041 resp->addULONG(1); // success
1042 resp->addString(newDir); //system depent do not convert
1050 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1052 delete[] dateDirName;
1053 delete[] titleDirName;
1054 delete[] newContainer;
1061 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1068 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1074 int VompClientRRProc::processGetChannelsList()
1078 char* chanConfig = x.config.getValueString("General", "Channels");
1080 if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");
1082 #if VDRVERSNUM >= 20301
1084 const cChannels* tChannels = Channels;
1086 const cChannels* tChannels = &Channels;
1089 for (const cChannel *channel = tChannels->First(); channel; channel = tChannels->Next(channel))
1091 #if VDRVERSNUM < 10300
1092 if (!channel->GroupSep() && (!channel->Ca() || allChans))
1094 if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
1097 log->log("RRProc", Log::DEBUG, "name: '%s'", channel->Name());
1099 if (channel->Vpid()) type = 1;
1100 #if VDRVERSNUM < 10300
1103 else if (channel->Apid(0)) type = 2;
1107 resp->addULONG(channel->Number());
1108 resp->addULONG(type);
1109 resp->addString(x.charconvsys->Convert(channel->Name()));
1110 #if VDRVERSNUM < 10703
1113 resp->addULONG(channel->Vtype());
1119 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1121 log->log("RRProc", Log::DEBUG, "Written channels list");
1126 int VompClientRRProc::processGetChannelPids()
1128 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1130 #if VDRVERSNUM >= 20301
1132 const cChannels* tChannels = Channels;
1134 cChannels* tChannels = &Channels;
1137 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1142 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1151 #if VDRVERSNUM < 10300
1153 log->log("RRProc", Log::DEBUG, "Apid1: %i", channel->Apid1());
1154 log->log("RRProc", Log::DEBUG, "Apid2: %i", channel->Apid2());
1156 if (channel->Apid2())
1158 else if (channel->Apid1())
1165 for (const int *Apid = channel->Apids(); *Apid; Apid++)
1169 for (const int *Dpid = channel->Dpids(); *Dpid; Dpid++)
1173 for (const int *Spid = channel->Spids(); *Spid; Spid++)
1180 // Format of response
1199 resp->addULONG(channel->Vpid());
1200 #if VDRVERSNUM < 10703
1203 resp->addULONG(channel->Vtype());
1205 resp->addULONG(numApids);
1207 #if VDRVERSNUM < 10300
1210 resp->addULONG(channel->Apid1());
1211 resp->addString("");
1215 resp->addULONG(channel->Apid2());
1216 resp->addString("");
1221 for (ULONG i = 0; i < numApids; i++)
1223 resp->addULONG(channel->Apid(i));
1224 resp->addString(x.charconvsys->Convert(channel->Alang(i)));
1226 resp->addULONG(numDpids);
1227 for (ULONG i = 0; i < numDpids; i++)
1229 resp->addULONG(channel->Dpid(i));
1230 resp->addString(x.charconvsys->Convert(channel->Dlang(i)));
1232 resp->addULONG(numSpids);
1233 for (ULONG i = 0; i < numSpids; i++)
1235 resp->addULONG(channel->Spid(i));
1236 resp->addString(x.charconvsys->Convert(channel->Slang(i)));
1239 resp->addULONG(channel->Tpid());
1240 // Format of extended response, for compatibility with older client at the end
1252 #if VDRVERSNUM < 10300
1262 for (ULONG i = 0; i < numApids; i++)
1264 #if VDRVERSNUM < 10715
1267 resp->addULONG(channel->Atype(i));
1270 for (ULONG i = 0; i < numDpids; i++)
1272 #if VDRVERSNUM < 10715
1273 resp->addULONG(0x6A /*AC3*/);
1275 resp->addULONG(channel->Dtype(i));
1278 for (ULONG i = 0; i < numSpids; i++)
1280 #if VDRVERSNUM < 10715
1285 resp->addULONG(channel->SubtitlingType(i));
1286 resp->addULONG(channel->CompositionPageId(i));
1287 resp->addULONG(channel->AncillaryPageId(i));
1294 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1296 log->log("RRProc", Log::DEBUG, "Written channels pids");
1301 int VompClientRRProc::processStartStreamingChannel()
1305 log->log("RRProc", Log::ERR, "Client called start streaming twice");
1309 log->log("RRProc", Log::DEBUG, "req->dataLength = %i", req->dataLength);
1310 ULONG channelNumber = ntohl(*(ULONG*)req->data);
1312 #if VDRVERSNUM >= 20301
1314 const cChannels* tChannels = Channels;
1316 cChannels* tChannels = &Channels;
1319 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1324 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1328 // get the priority we should use
1330 int priority = x.config.getValueLong("General", "Live priority", &fail);
1333 log->log("RRProc", Log::DEBUG, "Config: Live TV priority: %i", priority);
1337 log->log("RRProc", Log::DEBUG, "Config: Live TV priority config fail");
1341 // a bit of sanity..
1342 #if VDRVERSNUM < 10725
1343 if (priority < 0) priority = 0;
1345 if (priority < -99) priority = -99;
1347 if (priority > 99) priority = 99;
1349 log->log("RRProc", Log::DEBUG, "Using live TV priority %i", priority);
1350 x.lp = MVPReceiver::create(channel, priority);
1356 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1360 if (!x.lp->init(&x.tcp, req->requestID))
1366 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1372 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1376 int VompClientRRProc::processStopStreaming()
1378 log->log("RRProc", Log::DEBUG, "STOP STREAMING RECEIVED");
1381 x.lp->detachMVPReceiver();
1385 else if (x.recplayer)
1387 x.writeResumeData();
1395 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1399 int VompClientRRProc::processGetBlock()
1403 log->log("RRProc", Log::ERR, "Get block called during live streaming");
1409 log->log("RRProc", Log::ERR, "Get block called when no recording open");
1413 UCHAR* data = req->data;
1415 ULLONG position = x.ntohll(*(ULLONG*)data);
1416 data += sizeof(ULLONG);
1417 ULONG amount = ntohl(*(ULONG*)data);
1419 log->log("RRProc", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);
1421 UCHAR sendBuffer[amount];
1422 ULONG amountReceived = x.recplayer->getBlock(&sendBuffer[0], position, amount);
1424 if (!amountReceived)
1427 log->log("RRProc", Log::DEBUG, "written 4(0) as getblock got 0");
1431 resp->copyin(sendBuffer, amountReceived);
1432 log->log("RRProc", Log::DEBUG, "written %lu", amountReceived);
1436 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1437 log->log("RRProc", Log::DEBUG, "Finished getblock, have sent %lu", resp->getLen());
1441 int VompClientRRProc::processStartStreamingRecording()
1443 // data is a pointer to the fileName string
1445 #if VDRVERSNUM >= 20301
1446 LOCK_RECORDINGS_READ;
1447 const cRecordings* tRecordings = Recordings;
1449 cThreadLock RecordingsLock(&Recordings);
1450 cRecordings* tRecordings = &Recordings;
1453 const cRecording* recording = tRecordings->GetByName((char*)req->data);
1455 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
1459 x.recplayer = new RecPlayer(recording);
1461 resp->addULLONG(x.recplayer->getLengthBytes());
1462 resp->addULONG(x.recplayer->getLengthFrames());
1464 #if VDRVERSNUM < 10703
1465 resp->addUCHAR(true);//added for TS
1467 resp->addUCHAR(recording->IsPesRecording());//added for TS
1471 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1473 log->log("RRProc", Log::DEBUG, "written totalLength");
1477 // No such recording exists
1480 resp->addUCHAR(false);
1482 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1483 log->log("RRProc", Log::DEBUG, "start streaming recording failed");
1489 int VompClientRRProc::processPositionFromFrameNumber()
1493 ULONG frameNumber = ntohl(*(ULONG*)req->data);
1497 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1501 retval = x.recplayer->positionFromFrameNumber(frameNumber);
1504 resp->addULLONG(retval);
1506 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1508 log->log("RRProc", Log::DEBUG, "Wrote posFromFrameNum reply to client");
1512 int VompClientRRProc::processFrameNumberFromPosition()
1516 ULLONG position = x.ntohll(*(ULLONG*)req->data);
1520 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
1524 retval = x.recplayer->frameNumberFromPosition(position);
1527 resp->addULONG(retval);
1529 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1531 log->log("RRProc", Log::DEBUG, "Wrote frameNumFromPos reply to client");
1535 int VompClientRRProc::processGetIFrame()
1537 bool success = false;
1539 ULONG* data = (ULONG*)req->data;
1541 ULONG frameNumber = ntohl(*data);
1543 ULONG direction = ntohl(*data);
1545 ULLONG rfilePosition = 0;
1546 ULONG rframeNumber = 0;
1547 ULONG rframeLength = 0;
1551 log->log("RRProc", Log::DEBUG, "GetIFrame recording called when no recording being played!");
1555 success = x.recplayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
1558 // returns file position, frame number, length
1562 resp->addULLONG(rfilePosition);
1563 resp->addULONG(rframeNumber);
1564 resp->addULONG(rframeLength);
1572 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1574 log->log("RRProc", Log::DEBUG, "Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
1578 int VompClientRRProc::processGetChannelSchedule()
1580 ULONG* data = (ULONG*)req->data;
1582 ULONG channelNumber = ntohl(*data);
1584 ULONG startTime = ntohl(*data);
1586 ULONG duration = ntohl(*data);
1588 log->log("RRProc", Log::DEBUG, "get schedule called for channel %lu", channelNumber);
1590 #if VDRVERSNUM >= 20301
1592 const cChannels* tChannels = Channels;
1594 cChannels* tChannels = &Channels;
1597 const cChannel* channel = tChannels->GetByNumber(channelNumber);
1602 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1604 log->log("RRProc", Log::DEBUG, "written 0 because channel = NULL");
1608 log->log("RRProc", Log::DEBUG, "Got channel");
1610 #if VDRVERSNUM < 10300
1611 cMutexLock MutexLock;
1612 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
1613 #elif VDRVERSNUM < 20301
1614 cSchedulesLock MutexLock;
1615 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
1617 LOCK_SCHEDULES_READ;
1618 const cSchedules *tSchedules = Schedules;
1625 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1627 log->log("RRProc", Log::DEBUG, "written 0 because Schedule!s! = NULL");
1631 log->log("RRProc", Log::DEBUG, "Got schedule!s! object");
1633 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
1638 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1640 log->log("RRProc", Log::DEBUG, "written 0 because Schedule = NULL");
1644 log->log("RRProc", Log::DEBUG, "Got schedule object");
1646 const char* empty = "";
1647 bool atLeastOneEvent = false;
1650 ULONG thisEventTime;
1651 ULONG thisEventDuration;
1652 const char* thisEventTitle;
1653 const char* thisEventSubTitle;
1654 const char* thisEventDescription;
1656 #if VDRVERSNUM < 10300
1658 const cEventInfo *event;
1659 for (int eventNumber = 0; eventNumber < Schedule->NumEvents(); eventNumber++)
1661 event = Schedule->GetEventNumber(eventNumber);
1663 thisEventID = event->GetEventID();
1664 thisEventTime = event->GetTime();
1665 thisEventDuration = event->GetDuration();
1666 thisEventTitle = event->GetTitle();
1667 thisEventSubTitle = event->GetSubtitle();
1668 thisEventDescription = event->GetExtendedDescription();
1672 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1674 thisEventID = event->EventID();
1675 thisEventTime = event->StartTime();
1676 thisEventDuration = event->Duration();
1677 thisEventTitle = event->Title();
1678 thisEventSubTitle = NULL;
1679 thisEventDescription = event->Description();
1683 //in the past filter
1684 if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;
1687 if ((thisEventTime + thisEventDuration) <= startTime) continue;
1690 if (thisEventTime >= (startTime + duration)) continue;
1692 if (!thisEventTitle) thisEventTitle = empty;
1693 if (!thisEventSubTitle) thisEventSubTitle = empty;
1694 if (!thisEventDescription) thisEventDescription = empty;
1696 resp->addULONG(thisEventID);
1697 resp->addULONG(thisEventTime);
1698 resp->addULONG(thisEventDuration);
1700 resp->addString(x.charconvsys->Convert(thisEventTitle));
1701 resp->addString(x.charconvsys->Convert(thisEventSubTitle));
1702 resp->addString(x.charconvsys->Convert(thisEventDescription));
1704 atLeastOneEvent = true;
1707 log->log("RRProc", Log::DEBUG, "Got all event data");
1709 if (!atLeastOneEvent)
1712 log->log("RRProc", Log::DEBUG, "Written 0 because no data");
1716 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1718 log->log("RRProc", Log::DEBUG, "written schedules packet");
1723 int VompClientRRProc::processGetTimers()
1725 #if VDRVERSNUM >= 20301
1727 const cTimers* tTimers = Timers;
1729 const cTimers* tTimers = &Timers;
1732 const cTimer *timer;
1733 int numTimers = tTimers->Count();
1735 resp->addULONG(numTimers);
1737 for (int i = 0; i < numTimers; i++)
1739 timer = tTimers->Get(i);
1741 #if VDRVERSNUM < 10300
1742 resp->addULONG(timer->Active());
1744 resp->addULONG(timer->HasFlags(tfActive));
1746 resp->addULONG(timer->Recording());
1747 resp->addULONG(timer->Pending());
1748 resp->addULONG(timer->Priority());
1749 resp->addULONG(timer->Lifetime());
1750 resp->addULONG(timer->Channel()->Number());
1751 resp->addULONG(timer->StartTime());
1752 resp->addULONG(timer->StopTime());
1753 resp->addULONG(timer->Day());
1754 resp->addULONG(timer->WeekDays());
1755 resp->addString(timer->File()); //Filename is system specific and not visible by user
1759 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1761 log->log("RRProc", Log::DEBUG, "Written timers list");
1766 int VompClientRRProc::processSetTimer()
1768 char* timerString = new char[strlen((char*)req->data) + 1];
1769 strcpy(timerString, (char*)req->data);
1771 #if VDRVERSNUM < 10300
1773 // If this is VDR 1.2 the date part of the timer string must be reduced
1774 // to just DD rather than YYYY-MM-DD
1776 int s = 0; // source
1777 int d = 0; // destination
1779 while(c != 2) // copy up to date section, including the second ':'
1781 timerString[d] = req->data[s];
1782 if (req->data[s] == ':') c++;
1786 // now it has copied up to the date section
1788 while(c != 2) // waste YYYY-MM-
1790 if (req->data[s] == '-') c++;
1793 // now source is at the DD
1794 memcpy(&timerString[d], &req->data[s], req->dataLength - s);
1795 d += req->dataLength - s;
1796 timerString[d] = '\0';
1798 log->log("RRProc", Log::DEBUG, "Timer string after 1.2 conversion:");
1801 log->log("RRProc", Log::DEBUG, "%s", timerString);
1803 cTimer *timer = new cTimer;
1804 if (!timer->Parse((char*)timerString))
1808 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1813 #if VDRVERSNUM >= 20301
1815 cTimers* tTimers = Timers;
1817 cTimers* tTimers = &Timers;
1820 cTimer *t = tTimers->GetTimer(timer);
1825 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1830 timer->ClrFlags(tfRecording);
1831 tTimers->Add(timer);
1832 #if VDRVERSNUM < 10300
1834 #elif VDRVERSNUM < 20301
1835 tTimers->SetModified();
1840 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1844 int VompClientRRProc::processDeleteTimer()
1846 log->log("RRProc", Log::DEBUG, "Delete timer called");
1851 INT delChannel = ntohl(*(ULONG*)&req->data[position]); position += 4;
1852 INT delWeekdays = ntohl(*(ULONG*)&req->data[position]); position += 4;
1853 INT delDay = ntohl(*(ULONG*)&req->data[position]); position += 4;
1854 INT delStart = ntohl(*(ULONG*)&req->data[position]); position += 4;
1855 INT delStop = ntohl(*(ULONG*)&req->data[position]); position += 4;
1857 #if VDRVERSNUM >= 20301
1859 cTimers* tTimers = Timers;
1861 cTimers* tTimers = &Timers;
1865 for (ti = tTimers->First(); ti; ti = tTimers->Next(ti))
1867 if ( (ti->Channel()->Number() == delChannel)
1868 && ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
1869 && (ti->StartTime() == delStart)
1870 && (ti->StopTime() == delStop) )
1878 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1882 #if VDRVERSNUM < 20301
1883 // I suppose with the new locking this just can't happen
1884 if (tTimers->BeingEdited())
1886 log->log("RRProc", Log::ERR, "Unable to delete timer - timers being edited at VDR");
1889 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1894 if (ti->Recording())
1896 log->log("RRProc", Log::ERR, "Unable to delete timer - timer is running");
1899 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1904 tTimers->SetModified();
1908 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1912 int VompClientRRProc::processGetRecInfo()
1914 // data is a pointer to the fileName string
1915 #if VDRVERSNUM >= 20301
1916 LOCK_RECORDINGS_READ;
1917 const cRecordings* tRecordings = Recordings;
1919 cThreadLock RecordingsLock(&Recordings);
1920 cRecordings* tRecordings = &Recordings;
1923 const cRecording *recording = tRecordings->GetByName((char*)req->data);
1925 time_t timerStart = 0;
1926 time_t timerStop = 0;
1927 char* summary = NULL;
1928 char* shorttext = NULL;
1929 char* description = NULL;
1931 bool newsummary=false;
1932 ULONG resumePoint = 0;
1936 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
1939 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
1944 4 bytes: start time for timer
1945 4 bytes: end time for timer
1946 4 bytes: resume point
1948 4 bytes: num components
1955 8 bytes: frames per second
1959 // Get current timer
1961 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
1964 timerStart = rc->Timer()->StartTime();
1965 timerStop = rc->Timer()->StopTime();
1966 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
1969 resp->addULONG(timerStart);
1970 resp->addULONG(timerStop);
1974 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
1977 resumePoint = strtoul(value, NULL, 10);
1981 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
1984 ResumeId = atoi(ResumeIdC);
1988 ResumeId = 0; //default if not defined in vomp-MAC.conf
1990 while (ResumeIDLock)
1991 cCondWait::SleepMs(100);
1992 ResumeIDLock = true;
1993 int OldSetupResumeID = Setup.ResumeID;
1994 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
1995 #if VDRVERSNUM < 10703
1996 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
1998 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
2000 Setup.ResumeID = OldSetupResumeID; //and restore it back
2001 ResumeIDLock = false;
2003 int resume = ResumeFile.Read();
2004 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2006 resumePoint = ResumeFile.Read();
2008 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2010 resp->addULONG(resumePoint);
2014 #if VDRVERSNUM < 10300
2015 summary = (char*)recording->Summary();
2017 const cRecordingInfo *Info = recording->Info();
2018 shorttext = (char*)Info->ShortText();
2019 description = (char*) (char*)Info->Description();
2020 if (isempty(shorttext)) summary=description;
2021 else if (isempty(description)) summary=shorttext;
2023 int length=strlen(description)+strlen(shorttext)+4;
2024 summary=new char[length];
2025 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2029 if (isempty(summary)) summary = (char*)Info->Description();
2031 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2034 resp->addString(x.charconvsys->Convert(summary));
2035 if (newsummary) delete [] summary;
2039 resp->addString("");
2044 #if VDRVERSNUM < 10300
2046 // Send 0 for numchannels - this signals the client this info is not available
2050 const cComponents* components = Info->Components();
2052 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2060 resp->addULONG(components->NumComponents());
2062 tComponent* component;
2063 for (int i = 0; i < components->NumComponents(); i++)
2065 component = components->Component(i);
2067 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2069 resp->addUCHAR(component->stream);
2070 resp->addUCHAR(component->type);
2072 if (component->language)
2074 resp->addString(x.charconvsys->Convert(component->language));
2078 resp->addString("");
2080 if (component->description)
2082 resp->addString(x.charconvsys->Convert(component->description));
2086 resp->addString("");
2092 double framespersec;
2093 #if VDRVERSNUM < 10703
2094 framespersec = FRAMESPERSEC;
2096 framespersec = Info->FramesPerSecond();
2098 resp->adddouble(framespersec);
2099 title = (char*)Info->Title();
2102 resp->addString(x.charconvsys->Convert(title));
2106 resp->addString(x.charconvsys->Convert(recording->Name()));
2112 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2114 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2119 // TODO Remove getrecinfo(1) for version 0.6.0 and rename getrecinfo2 to getrecinfo
2121 int VompClientRRProc::processGetRecInfo2()
2123 // data is a pointer to the fileName string
2124 #if VDRVERSNUM >= 20301
2125 LOCK_RECORDINGS_READ;
2126 const cRecordings* tRecordings = Recordings;
2128 cThreadLock RecordingsLock(&Recordings);
2129 cRecordings* tRecordings = &Recordings;
2132 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2134 time_t timerStart = 0;
2135 time_t timerStop = 0;
2136 char* summary = NULL;
2137 char* shorttext = NULL;
2138 char* description = NULL;
2140 bool newsummary=false;
2141 ULONG resumePoint = 0;
2145 log->log("RRProc", Log::ERR, "GetRecInfo found no recording");
2148 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2153 4 bytes: start time for timer
2154 4 bytes: end time for timer
2155 4 bytes: resume point
2157 4 bytes: num components
2164 8 bytes: frames per second
2166 // new stuff starts here
2167 string: channel name
2174 // Get current timer
2176 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
2179 timerStart = rc->Timer()->StartTime();
2180 timerStop = rc->Timer()->StopTime();
2181 log->log("RRProc", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
2184 resp->addULONG(timerStart);
2185 resp->addULONG(timerStop);
2189 /* char* value = x.config.getValueString("ResumeData", (char*)req->data);
2192 resumePoint = strtoul(value, NULL, 10);
2196 char* ResumeIdC = x.config.getValueString("General", "ResumeId");
2199 ResumeId = atoi(ResumeIdC);
2203 ResumeId = 0; //default if not defined in vomp-MAC.conf
2205 while (ResumeIDLock)
2206 cCondWait::SleepMs(100);
2207 ResumeIDLock = true;
2208 int OldSetupResumeID = Setup.ResumeID;
2209 Setup.ResumeID = ResumeId; //UGLY: quickly change resumeid
2210 #if VDRVERSNUM < 10703
2211 cResumeFile ResumeFile(recording->FileName()); //get corresponding resume file
2213 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording()); //get corresponding resume file
2215 Setup.ResumeID = OldSetupResumeID; //and restore it back
2216 ResumeIDLock = false;
2218 int resume = ResumeFile.Read();
2219 //isyslog("VOMPDEBUG: resumePoint = %i, resume = %i, ResumeId = %i",resumePoint, resume, ResumeId);
2221 resumePoint = ResumeFile.Read();
2223 log->log("RRProc", Log::DEBUG, "GRI: RP: %lu", resumePoint);
2225 resp->addULONG(resumePoint);
2229 #if VDRVERSNUM < 10300
2230 summary = (char*)recording->Summary();
2232 const cRecordingInfo *Info = recording->Info();
2233 shorttext = (char*)Info->ShortText();
2234 description = (char*) (char*)Info->Description();
2235 if (isempty(shorttext)) summary=description;
2236 else if (isempty(description)) summary=shorttext;
2238 int length=strlen(description)+strlen(shorttext)+4;
2239 summary=new char[length];
2240 snprintf(summary,length,"%s\n\n%s",shorttext,description);
2244 if (isempty(summary)) summary = (char*)Info->Description();
2246 log->log("RRProc", Log::DEBUG, "GRI: S: %s", summary);
2249 resp->addString(x.charconvsys->Convert(summary));
2250 if (newsummary) delete [] summary;
2254 resp->addString("");
2259 #if VDRVERSNUM < 10300
2261 // Send 0 for numchannels - this signals the client this info is not available
2265 const cComponents* components = Info->Components();
2267 log->log("RRProc", Log::DEBUG, "GRI: D1: %p", components);
2275 resp->addULONG(components->NumComponents());
2277 tComponent* component;
2278 for (int i = 0; i < components->NumComponents(); i++)
2280 component = components->Component(i);
2282 log->log("RRProc", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
2284 resp->addUCHAR(component->stream);
2285 resp->addUCHAR(component->type);
2287 if (component->language)
2289 resp->addString(x.charconvsys->Convert(component->language));
2293 resp->addString("");
2295 if (component->description)
2297 resp->addString(x.charconvsys->Convert(component->description));
2301 resp->addString("");
2307 double framespersec;
2308 #if VDRVERSNUM < 10703
2309 framespersec = FRAMESPERSEC;
2311 framespersec = Info->FramesPerSecond();
2313 resp->adddouble(framespersec);
2314 title = (char*)Info->Title();
2317 resp->addString(x.charconvsys->Convert(title));
2321 resp->addString(x.charconvsys->Convert(recording->Name()));
2325 if (Info->ChannelName())
2327 resp->addString(x.charconvsys->Convert(Info->ChannelName()));
2331 resp->addString("");
2334 resp->addULONG(recording->LengthInSeconds());
2335 resp->addULONG(recording->FileSizeMB());
2336 resp->addULONG(recording->Priority());
2337 resp->addULONG(recording->Lifetime());
2342 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2344 log->log("RRProc", Log::DEBUG, "Written getrecinfo");
2351 int VompClientRRProc::processReScanRecording()
2355 log->log("RRProc", Log::DEBUG, "Rescan recording called when no recording being played!");
2359 x.recplayer->scan();
2361 resp->addULLONG(x.recplayer->getLengthBytes());
2362 resp->addULONG(x.recplayer->getLengthFrames());
2364 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2365 log->log("RRProc", Log::DEBUG, "Rescan recording, wrote new length to client");
2369 // FIXME without client calling rescan, getblock wont work even tho more data is avail
2371 int VompClientRRProc::processGetMarks()
2373 // data is a pointer to the fileName string
2374 #if VDRVERSNUM >= 20301
2375 LOCK_RECORDINGS_READ;
2376 const cRecordings* tRecordings = Recordings;
2378 cThreadLock RecordingsLock(&Recordings);
2379 cRecordings* tRecordings = &Recordings;
2382 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2383 log->log("RRProc", Log::DEBUG, "recording pointer %p", recording);
2388 #if VDRVERSNUM < 10703
2389 Marks.Load(recording->FileName());
2391 Marks.Load(recording->FileName(), recording->FramesPerSecond(), recording->IsPesRecording());
2395 for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
2397 #if VDRVERSNUM < 10721
2398 ULLONG mposition = m->position;
2400 ULLONG mposition = m->Position();
2402 log->log("RRProc", Log::DEBUG, "found Mark %i", mposition);
2404 resp->addULONG(mposition);
2409 log->log("RRProc", Log::DEBUG, "no marks found, sending 0-mark");
2415 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2417 log->log("RRProc", Log::DEBUG, "Written Marks list");
2422 int VompClientRRProc::processVDRShutdown()
2424 log->log("RRProc", Log::DEBUG, "Trying to shutdown VDR");
2425 VompClient::decClients(); // Temporarily make this client disappear
2426 cRemote::Put(kPower);
2427 VompClient::incClients();
2429 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2433 int VompClientRRProc::processGetRecScraperEventType()
2435 #if VDRVERSNUM >= 20301
2436 LOCK_RECORDINGS_READ;
2437 const cRecordings* tRecordings = Recordings;
2439 cThreadLock RecordingsLock(&Recordings);
2440 cRecordings* tRecordings = &Recordings;
2443 const cRecording *recording = tRecordings->GetByName((char*)req->data);
2444 ScraperGetEventType call;
2447 if (recording && x.scrapQuery())
2449 call.recording = recording;
2450 x.scraper->Service("GetEventType", &call);
2452 resp->addUCHAR(call.type);
2453 if (call.type == tMovie)
2455 resp->addLONG(call.movieId);
2457 else if (call.type == tSeries)
2459 resp->addLONG(call.seriesId);
2460 resp->addLONG(call.episodeId);
2463 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2468 int VompClientRRProc::processGetEventScraperEventType()
2470 ScraperGetEventType call;
2472 ULONG channelid = ntohl(*(ULONG*)req->data);
2473 ULONG eventid = ntohl(*(ULONG*)(req->data+4));
2474 const cEvent *event = NULL;
2476 #if VDRVERSNUM >= 20301
2478 const cChannels* tChannels = Channels;
2480 cChannels* tChannels = &Channels;
2483 const cChannel* channel = tChannels->GetByNumber(channelid);
2485 #if VDRVERSNUM < 10300
2486 cMutexLock MutexLock;
2487 const cSchedules *tSchedules = cSIProcessor::Schedules(MutexLock);
2488 #elif VDRVERSNUM < 20301
2489 cSchedulesLock MutexLock;
2490 const cSchedules *tSchedules = cSchedules::Schedules(MutexLock);
2492 LOCK_SCHEDULES_READ;
2493 const cSchedules *tSchedules = Schedules;
2496 if (tSchedules && channel)
2498 const cSchedule *Schedule = tSchedules->GetSchedule(channel->GetChannelID());
2500 event = Schedule->GetEvent(eventid);
2504 if (event && x.scrapQuery())
2507 x.scraper->Service("GetEventType",&call);
2509 resp->addUCHAR(call.type);
2510 if (call.type == tMovie)
2512 resp->addLONG(call.movieId);
2513 } else if (call.type == tSeries){
2514 resp->addLONG(call.seriesId);
2515 resp->addLONG(call.episodeId);
2517 if (x.pict->epgImageExists(eventid)) {
2524 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2529 #define ADDSTRING_TO_PAKET(y) if ((y)!=0) resp->addString(x.charconvutf8->Convert(y)); else resp->addString("");
2531 int VompClientRRProc::processGetScraperMovieInfo()
2535 movie.movieId = ntohl(*(ULONG*)req->data);
2536 if (!x.scrapQuery()) {
2537 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2538 return 0; //stupid, I have no scraper why are you still asking
2540 x.scraper->Service("GetMovie",&movie);
2543 ADDSTRING_TO_PAKET(movie.title.c_str());
2544 ADDSTRING_TO_PAKET(movie.originalTitle.c_str());
2545 ADDSTRING_TO_PAKET(movie.tagline.c_str());
2546 ADDSTRING_TO_PAKET(movie.overview.c_str());
2547 resp->addUCHAR(movie.adult);
2548 ADDSTRING_TO_PAKET(movie.collectionName.c_str());
2550 resp->addLONG(movie.budget);
2551 resp->addLONG(movie.revenue);
2552 ADDSTRING_TO_PAKET(movie.genres.c_str());
2553 ADDSTRING_TO_PAKET(movie.homepage.c_str());
2554 ADDSTRING_TO_PAKET(movie.releaseDate.c_str());
2555 resp->addLONG(movie.runtime);
2556 resp->adddouble(movie.popularity);
2557 resp->adddouble(movie.voteAverage);
2558 resp->addULONG(movie.poster.width);
2559 resp->addULONG(movie.poster.height);
2560 resp->addULONG(movie.fanart.width);
2561 resp->addULONG(movie.fanart.height);
2562 resp->addULONG(movie.collectionPoster.width);
2563 resp->addULONG(movie.collectionPoster.height);
2564 resp->addULONG(movie.collectionFanart.width);
2565 resp->addULONG(movie.collectionFanart.height);
2566 resp->addULONG(movie.actors.size());
2567 for (ULONG acty=0; acty < movie.actors.size(); acty++) {
2568 ADDSTRING_TO_PAKET(movie.actors[acty].name.c_str());
2569 ADDSTRING_TO_PAKET(movie.actors[acty].role.c_str());
2570 resp->addULONG(movie.actors[acty].actorThumb.width);
2571 resp->addULONG(movie.actors[acty].actorThumb.height);
2575 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2582 int VompClientRRProc::processGetScraperSeriesInfo()
2585 series.seriesId = ntohl(*(ULONG*)req->data);
2586 series.episodeId = ntohl(*(ULONG*)(req->data+4));
2587 if (!x.scrapQuery()) {
2588 log->log("RRProc", Log::DEBUG, "No Scraper, get SeriesInfo");
2589 return 0; //stupid, I have no scraper why are you still asking
2591 x.scraper->Service("GetSeries",&series);
2593 ADDSTRING_TO_PAKET(series.name.c_str());
2594 ADDSTRING_TO_PAKET(series.overview.c_str());
2595 ADDSTRING_TO_PAKET(series.firstAired.c_str());
2596 ADDSTRING_TO_PAKET(series.network.c_str());
2597 ADDSTRING_TO_PAKET(series.genre.c_str());
2598 resp->adddouble(series.rating);
2599 ADDSTRING_TO_PAKET(series.status.c_str());
2601 resp->addLONG(series.episode.number);
2602 resp->addLONG(series.episode.season);
2603 ADDSTRING_TO_PAKET(series.episode.name.c_str());
2604 ADDSTRING_TO_PAKET(series.episode.firstAired.c_str());
2605 ADDSTRING_TO_PAKET(series.episode.guestStars.c_str());
2606 ADDSTRING_TO_PAKET(series.episode.overview.c_str());
2607 resp->adddouble(series.episode.rating);
2608 resp->addULONG(series.episode.episodeImage.width);
2609 resp->addULONG(series.episode.episodeImage.height);
2611 ULONG num_actors = series.actors.size();
2612 resp->addULONG(num_actors);
2613 for (ULONG acty=0; acty < num_actors; acty++) {
2614 ADDSTRING_TO_PAKET(series.actors[acty].name.c_str());
2615 ADDSTRING_TO_PAKET(series.actors[acty].role.c_str());
2616 resp->addULONG(series.actors[acty].actorThumb.width);
2617 resp->addULONG(series.actors[acty].actorThumb.height);
2619 ULONG num_posters = series.posters.size();
2620 resp->addULONG(num_posters);
2621 for (ULONG medias = 0; medias < num_posters; medias++ ) {
2622 cTvMedia media=series.posters[medias];
2623 resp->addULONG(media.width);
2624 resp->addULONG(media.height);
2627 ULONG num_banners = series.banners.size();
2628 resp->addULONG(num_banners);
2629 for (ULONG medias = 0; medias < num_banners; medias++ ) {
2630 cTvMedia media=series.banners[medias];
2631 resp->addULONG(media.width);
2632 resp->addULONG(media.height);
2634 ULONG num_fanarts = series.fanarts.size();
2635 resp->addULONG(num_fanarts);
2636 for (ULONG medias = 0; medias < num_fanarts; medias++ ) {
2637 cTvMedia media=series.fanarts[medias];
2638 resp->addULONG(media.width);
2639 resp->addULONG(media.height);
2641 resp->addULONG(series.seasonPoster.width);
2642 resp->addULONG(series.seasonPoster.height);
2646 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2651 int VompClientRRProc::processLoadTvMedia()
2653 TVMediaRequest tvreq;
2654 tvreq.streamID = req->requestID;
2655 tvreq.type = ntohl(*(ULONG*)req->data);
2656 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2657 tvreq.secondary_id = ntohl(*(ULONG*)(req->data+8));
2658 tvreq.type_pict = ntohl(*(ULONG*)(req->data+12));
2659 tvreq.container = ntohl(*(ULONG*)(req->data+16));
2660 tvreq.container_member = ntohl(*(ULONG*)(req->data+20));
2661 log->log("RRProc", Log::DEBUG, "TVMedia request %d",req->requestID);
2662 x.pict->addTVMediaRequest(tvreq);
2667 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2672 int VompClientRRProc::processLoadTvMediaRecThumb()
2674 TVMediaRequest tvreq;
2675 tvreq.streamID = req->requestID;
2676 tvreq.type = 3; // unknown but primary_name is set
2677 tvreq.primary_id = 0;
2678 tvreq.primary_name = std::string((const char*) req->data);
2679 tvreq.secondary_id = 0;
2680 tvreq.type_pict = 1;
2681 tvreq.container = 0;
2682 tvreq.container_member = 0;
2683 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2684 x.pict->addTVMediaRequest(tvreq);
2689 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2694 int VompClientRRProc::processLoadTvMediaEventThumb()
2696 TVMediaRequest tvreq;
2697 tvreq.streamID = req->requestID;
2698 tvreq.type = 4; // unknown but primary_id is set
2699 UINT channelid = ntohl(*(ULONG*)req->data);
2700 tvreq.primary_id = ntohl(*(ULONG*)(req->data+4));
2701 tvreq.secondary_id = 0;
2703 #if VDRVERSNUM >= 20301
2705 const cChannels* tChannels = Channels;
2707 cChannels* tChannels = &Channels;
2710 const cChannel* channel = tChannels->GetByNumber(channelid);
2712 if (channel) tvreq.primary_name = std::string((const char*)channel->GetChannelID().ToString());
2713 tvreq.type_pict = 1;
2714 tvreq.container = 0;
2715 tvreq.container_member = 0;
2716 log->log("RRProc", Log::DEBUG, "TVMedia request %d %s",req->requestID,req->data);
2717 x.pict->addTVMediaRequest(tvreq);
2722 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2727 int VompClientRRProc::processLoadChannelLogo()
2729 TVMediaRequest tvreq;
2730 tvreq.streamID = req->requestID;
2731 tvreq.type = 5; // channel logo
2732 UINT channelid = ntohl(*(ULONG*)req->data);
2733 tvreq.primary_id = channelid;
2734 tvreq.secondary_id = 0;
2736 #if VDRVERSNUM >= 20301
2738 const cChannels* tChannels = Channels;
2740 cChannels* tChannels = &Channels;
2743 const cChannel* channel = tChannels->GetByNumber(channelid);
2745 if (channel) tvreq.primary_name = std::string((const char*)channel->Name());
2746 tvreq.type_pict = 1;
2747 tvreq.container = 0;
2748 tvreq.container_member = 0;
2749 if (channel) log->log("RRProc", Log::DEBUG, "TVMedia request %d %d %s",req->requestID,channelid, channel->Name());
2750 x.pict->addTVMediaRequest(tvreq);
2755 x.tcp.sendPacket(resp->getPtr(), resp->getLen());
2763 #endif // !VOMPSTANDALONE