17 #include <vdr/plugin.h>
18 #include <vdr/videodir.h>
19 #include <vdr/recording.h>
21 #include <vdr/timers.h>
23 VDRClient::VDRClient()
25 logger = spd::get("jsonserver_spdlog");
28 VDRClient::~VDRClient()
30 logger->debug("VDRClient destructor");
33 bool VDRClient::process(std::string& request, PFMap& postFields, std::string& returnString)
35 Json::Value returnJSON;
40 if (request == "gettime") success = gettime(postFields, returnJSON);
41 else if (request == "diskstats") success = diskstats(postFields, returnJSON);
42 else if (request == "channellist") success = channellist(postFields, returnJSON);
43 else if (request == "reclist") success = reclist(postFields, returnJSON);
44 else if (request == "timerlist") success = timerlist(postFields, returnJSON);
45 else if (request == "epgdownload") success = epgdownload(postFields, returnJSON);
46 else if (request == "tunersstatus") success = tunersstatus(postFields, returnJSON);
47 else if (request == "epgfilterget") success = epgfilterget(postFields, returnJSON);
49 else if (request == "channelschedule") success = channelschedule(postFields, returnJSON);
50 else if (request == "getscheduleevent") success = getscheduleevent(postFields, returnJSON);
51 else if (request == "epgsearch") success = epgsearch(postFields, returnJSON);
52 else if (request == "epgsearchsame") success = epgsearchsame(postFields, returnJSON);
53 else if (request == "epgsearchotherhalf")success = epgsearchotherhalf(postFields, returnJSON);
54 else if (request == "timerset") success = timerset(postFields, returnJSON);
55 else if (request == "recinfo") success = recinfo(postFields, returnJSON);
56 else if (request == "recstop") success = recstop(postFields, returnJSON);
57 else if (request == "recdel") success = recdel(postFields, returnJSON);
58 else if (request == "recrename") success = recrename(postFields, returnJSON);
59 else if (request == "recmove") success = recmove(postFields, returnJSON);
60 else if (request == "timersetactive") success = timersetactive(postFields, returnJSON);
61 else if (request == "timerdel") success = timerdel(postFields, returnJSON);
62 else if (request == "timerisrecording") success = timerisrecording(postFields, returnJSON);
63 else if (request == "recresetresume") success = recresetresume(postFields, returnJSON);
64 else if (request == "timeredit") success = timeredit(postFields, returnJSON);
65 else if (request == "epgfilteradd") success = epgfilteradd(postFields, returnJSON);
66 else if (request == "epgfilterdel") success = epgfilterdel(postFields, returnJSON);
68 catch (BadParamException e)
70 logger->error("Bad parameter in call, paramName: {}", e.param);
71 returnJSON["Result"] = false;
72 returnJSON["Error"] = "Bad request parameter";
73 returnJSON["Detail"] = e.param;
77 if (!success) return false;
79 Json::StyledWriter sw;
80 returnString = sw.write(returnJSON);
81 logger->debug("Done sw write");
85 bool VDRClient::gettime(PFMap& postFields, Json::Value& js)
87 logger->debug("get_time");
90 gettimeofday(&tv, NULL);
92 js["Time"] = (Json::UInt64)tv.tv_sec;
93 js["MTime"] = (Json::UInt)(tv.tv_usec/1000);
98 bool VDRClient::diskstats(PFMap& postFields, Json::Value& js)
100 logger->debug("diskstats");
104 int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB, &UsedMB);
106 js["FreeMiB"] = FreeMB;
107 js["UsedMiB"] = UsedMB;
108 js["Percent"] = Percent;
113 bool VDRClient::channellist(PFMap& postFields, Json::Value& js)
115 logger->debug("channellist");
117 Json::Value jschannels(Json::arrayValue);
119 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
121 if (!channel->GroupSep())
123 Json::Value oneChannel;
124 oneChannel["ID"] = (const char *)channel->GetChannelID().ToString();
125 oneChannel["Number"] = channel->Number();
126 oneChannel["Name"] = channel->Name();
127 jschannels.append(oneChannel);
130 js["Channels"] = jschannels;
135 bool VDRClient::reclist(PFMap& postFields, Json::Value& js)
137 logger->debug("reclist");
139 Json::Value jsrecordings(Json::arrayValue);
140 cRecordings Recordings;
142 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
145 oneRec["StartTime"] = (Json::UInt)recording->Start();
146 oneRec["Length"] = (Json::UInt)recording->LengthInSeconds();
147 oneRec["IsNew"] = recording->IsNew();
148 oneRec["Name"] = recording->Name();
149 oneRec["Filename"] = recording->FileName();
150 oneRec["FileSizeMB"] = recording->FileSizeMB();
152 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
153 if (rc) oneRec["CurrentlyRecording"] = true;
154 else oneRec["CurrentlyRecording"] = false;
156 jsrecordings.append(oneRec);
158 js["Recordings"] = jsrecordings;
163 bool VDRClient::timerlist(PFMap& postFields, Json::Value& js)
165 logger->debug("timerlist");
167 Json::Value jstimers(Json::arrayValue);
170 int numTimers = Timers.Count();
172 for (int i = 0; i < numTimers; i++)
174 timer = Timers.Get(i);
175 Json::Value oneTimer;
176 oneTimer["Active"] = timer->HasFlags(tfActive);
177 oneTimer["Recording"] = timer->Recording();
178 oneTimer["Pending"] = timer->Pending();
179 oneTimer["Priority"] = timer->Priority();
180 oneTimer["Lifetime"] = timer->Lifetime();
181 oneTimer["ChannelNumber"] = timer->Channel()->Number();
182 oneTimer["ChannelID"] = (const char *)timer->Channel()->GetChannelID().ToString();
183 oneTimer["StartTime"] = (int)timer->StartTime();
184 oneTimer["StopTime"] = (int)timer->StopTime();
185 oneTimer["Day"] = (int)timer->Day();
186 oneTimer["WeekDays"] = timer->WeekDays();
187 oneTimer["Name"] = timer->File();
189 const cEvent* event = timer->Event();
192 oneTimer["EventID"] = event->EventID();
196 int channelNumber = timer->Channel()->Number();
197 int aroundTime = timer->StartTime() + 1;
198 const cEvent* eventAround = getEvent(js, channelNumber, 0, aroundTime);
201 oneTimer["EventID"] = eventAround->EventID();
205 oneTimer["EventID"] = 0;
209 jstimers.append(oneTimer);
212 js["Timers"] = jstimers;
213 js["NumTuners"] = cDevice::NumDevices();
218 bool VDRClient::channelschedule(PFMap& postFields, Json::Value& js) // RETHROWS
220 logger->debug("channelschedule");
221 int channelNumber = getVarInt(postFields, "channelnumber");
222 int startTime = getVarInt(postFields, "starttime");
223 int duration = getVarInt(postFields, "duration");
225 cChannel* channel = NULL;
226 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
228 if (channel->GroupSep()) continue;
229 if (channel->Number() == channelNumber) break;
234 logger->error("channelschedule: Could not find requested channel: {}", channelNumber);
235 js["Result"] = false;
236 js["Error"] = "Could not find channel";
240 cSchedulesLock MutexLock;
241 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
244 logger->error("channelschedule: Could not find requested channel: {}", channelNumber);
245 js["Result"] = false;
246 js["Error"] = "Internal schedules error (1)";
249 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
252 logger->error("channelschedule: Could not find requested channel: {}", channelNumber);
253 js["Result"] = false;
254 js["Error"] = "Internal schedules error (2)";
258 Json::Value jsevents(Json::arrayValue);
260 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
262 if ((event->StartTime() + event->Duration()) < time(NULL)) continue; //in the past filter
263 if ((event->StartTime() + event->Duration()) <= startTime) continue; //start time filter
264 if (event->StartTime() >= (startTime + duration)) continue; //duration filter
266 Json::Value oneEvent;
267 oneEvent["ID"] = event->EventID();
268 oneEvent["Time"] = (Json::UInt)event->StartTime();
269 oneEvent["Duration"] = event->Duration();
270 oneEvent["Title"] = event->Title() ? event->Title() : "";
271 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
272 oneEvent["HasTimer"] = event->HasTimer();
273 jsevents.append(oneEvent);
277 js["Events"] = jsevents;
281 bool VDRClient::getscheduleevent(PFMap& postFields, Json::Value& js) // RETHROWS
283 logger->debug("getscheduleevent");
285 int channelNumber = getVarInt(postFields, "channelnumber");
286 int eventID = getVarInt(postFields, "eventid");
288 const cEvent* event = getEvent(js, channelNumber, eventID, 0);
291 js["Result"] = false;
295 Json::Value oneEvent;
296 oneEvent["ID"] = event->EventID();
297 oneEvent["Time"] = (Json::UInt)event->StartTime();
298 oneEvent["Duration"] = event->Duration();
299 oneEvent["Title"] = event->Title() ? event->Title() : "";
300 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
301 oneEvent["Description"] = event->Description() ? event->Description() : "";
302 oneEvent["HasTimer"] = event->HasTimer();
303 oneEvent["RunningStatus"] = event->RunningStatus();
306 js["Event"] = oneEvent;
310 bool VDRClient::epgdownload(PFMap& postFields, Json::Value& js)
312 logger->debug("epgdownload");
314 cSchedulesLock MutexLock;
315 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
319 logger->error("epgdownload: Could not get Schedules object");
320 js["Result"] = false;
321 js["Error"] = "Internal schedules error (1)";
325 Json::Value jsevents(Json::arrayValue);
327 for (cChannel* channel = Channels.First(); channel; channel = Channels.Next(channel))
329 if (channel->GroupSep()) continue;
331 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
332 if (!Schedule) continue;
335 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
337 Json::Value oneEvent;
338 oneEvent["ChannelNumber"] = channel->Number();
339 oneEvent["ChannelID"] = (const char*)event->ChannelID().ToString();
340 oneEvent["ID"] = event->EventID();
341 oneEvent["Time"] = (Json::UInt)event->StartTime();
342 oneEvent["Duration"] = event->Duration();
343 oneEvent["Title"] = event->Title() ? event->Title() : "";
344 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
345 oneEvent["HasTimer"] = event->HasTimer();
346 oneEvent["Description"] = event->Description() ? event->Description() : "";
347 jsevents.append(oneEvent);
352 js["Events"] = jsevents;
356 bool VDRClient::epgsearch(PFMap& postFields, Json::Value& js) // RETHROWS
358 logger->debug("epgsearch");
360 std::string searchfor = getVarString(postFields, "searchfor");
361 logger->debug("epgsearch: search for: {}", searchfor);
363 cSchedulesLock MutexLock;
364 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
368 logger->error("epgsearch: Could not get Schedules object");
369 js["Result"] = false;
370 js["Error"] = "Internal schedules error (1)";
374 Json::Value jsevents(Json::arrayValue);
376 for (cChannel* channel = Channels.First(); channel; channel = Channels.Next(channel))
378 if (channel->GroupSep()) continue;
380 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
381 if (!Schedule) continue;
384 bool founddescription;
385 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
388 founddescription = false;
390 if (event->Title() && strcasestr(event->Title(), searchfor.c_str())) foundtitle = true;
392 if (!foundtitle && event->Description())
393 if (strcasestr(event->Description(), searchfor.c_str())) founddescription = true;
395 if (foundtitle || founddescription)
397 Json::Value oneEvent;
398 oneEvent["ChannelNumber"] = channel->Number();
399 oneEvent["ChannelID"] = (const char*)event->ChannelID().ToString();
400 oneEvent["ID"] = event->EventID();
401 oneEvent["Time"] = (Json::UInt)event->StartTime();
402 oneEvent["Duration"] = event->Duration();
403 oneEvent["Title"] = event->Title() ? event->Title() : "";
404 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
405 oneEvent["HasTimer"] = event->HasTimer();
406 oneEvent["Description"] = event->Description() ? event->Description() : "";
407 if (founddescription)
408 oneEvent["FoundInDesc"] = true;
410 oneEvent["FoundInDesc"] = false;
411 jsevents.append(oneEvent);
417 js["Events"] = jsevents;
419 logger->debug("epgsearch: search for: {} done", searchfor);
424 bool VDRClient::epgsearchsame(PFMap& postFields, Json::Value& js) // RETHROWS
426 logger->debug("epgsearchsame");
428 int atTime = getVarInt(postFields, "time");
429 std::string sTitle = getVarString(postFields, "title");
431 logger->debug("epgsearchsame: request time: {}, title: {}", atTime, sTitle);
433 cSchedulesLock SchedulesLock;
434 const cSchedules *schedules = cSchedules::Schedules(SchedulesLock);
437 js["Result"] = false;
441 Json::Value jsevents(Json::arrayValue);
444 for (cSchedule *schedule = schedules->First(); (schedule != NULL); schedule = schedules->Next(schedule))
446 event = schedule->GetEventAround(atTime);
447 if (!event) continue; // nothing found on this schedule(channel)
449 if (!strcmp(event->Title(), sTitle.c_str()))
451 Json::Value oneEvent;
452 oneEvent["ChannelID"] = (const char*)event->ChannelID().ToString();
453 oneEvent["ID"] = event->EventID();
454 oneEvent["Time"] = (Json::UInt)event->StartTime();
455 oneEvent["Duration"] = event->Duration();
456 oneEvent["Title"] = event->Title() ? event->Title() : "";
457 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
458 oneEvent["HasTimer"] = event->HasTimer();
459 //oneEvent["Description"] = event->Description() ? event->Description() : "";
460 jsevents.append(oneEvent);
464 js["Events"] = jsevents;
469 bool VDRClient::epgsearchotherhalf(PFMap& postFields, Json::Value& js) // RETHROWS
471 logger->debug("epgsearchotherhalf");
472 int channelNumber = getVarInt(postFields, "channelnumber");
473 int eventID = getVarInt(postFields, "eventid");
475 cChannel* channel = NULL;
476 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
478 if (channel->GroupSep()) continue;
479 if (channel->Number() == channelNumber) break;
484 logger->error("epgsearchotherhalf: Could not find requested channel: {}", channelNumber);
485 js["Result"] = false;
486 js["Error"] = "Could not find channel";
490 cSchedulesLock MutexLock;
491 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
494 logger->error("epgsearchotherhalf: Could not find requested channel: {}", channelNumber);
495 js["Result"] = false;
496 js["Error"] = "Internal schedules error (1)";
499 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
502 logger->error("epgsearchotherhalf: Could not find requested channel: {}", channelNumber);
503 js["Result"] = false;
504 js["Error"] = "Internal schedules error (2)";
508 js["OtherEventFound"] = false;
510 const cEvent* eventM1 = NULL;
511 const cEvent* eventM2 = NULL;
512 const cEvent* otherEvent = NULL;
514 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
516 if (event->EventID() == (unsigned long)eventID)
518 if ((eventM2 != NULL) && (!strcmp(eventM2->Title(), event->Title())))
520 otherEvent = eventM2;
524 const cEvent* eventP1 = Schedule->Events()->Next(event);
527 const cEvent* eventP2 = Schedule->Events()->Next(eventP1);
529 if (eventP2 && (!strcmp(eventP2->Title(), event->Title())))
531 otherEvent = eventP2;
538 Json::Value oneEvent;
539 oneEvent["ID"] = otherEvent->EventID();
540 oneEvent["ChannelNumber"] = channel->Number();
541 oneEvent["Time"] = (Json::UInt)otherEvent->StartTime();
542 oneEvent["Duration"] = otherEvent->Duration();
543 oneEvent["Title"] = otherEvent->Title() ? otherEvent->Title() : "";
544 oneEvent["HasTimer"] = otherEvent->HasTimer();
545 js["Event"] = oneEvent;
546 js["OtherEventFound"] = true;
560 bool VDRClient::tunersstatus(PFMap& postFields, Json::Value& js)
562 logger->debug("tunerstatus");
564 js["NumDevices"] = cDevice::NumDevices();
566 Json::Value jsdevices(Json::arrayValue);
568 for (int i = 0; i < cDevice::NumDevices(); i++)
570 Json::Value oneDevice;
571 cDevice *d = cDevice::GetDevice(i);
572 oneDevice["Number"] = d->DeviceNumber();
573 oneDevice["Type"] = (const char*)d->DeviceType();
574 oneDevice["Name"] = (const char*)d->DeviceName();
575 oneDevice["IsPrimary"] = d->IsPrimaryDevice();
577 const cChannel* cchannel = d->GetCurrentlyTunedTransponder();
580 oneDevice["Frequency"] = cchannel->Frequency();
581 oneDevice["SignalStrength"] = d->SignalStrength();
582 oneDevice["SignalQuality"] = d->SignalQuality();
587 oneDevice["Frequency"] = 0;
588 oneDevice["SignalStrength"] = 0;
589 oneDevice["SignalQuality"] = 0;
592 jsdevices.append(oneDevice);
595 js["Devices"] = jsdevices;
598 Json::Value jstimers(Json::arrayValue);
599 int numTimers = Timers.Count();
601 for (int i = 0; i < numTimers; i++)
603 timer = Timers.Get(i);
605 if (timer->Recording())
607 Json::Value oneTimer;
608 oneTimer["Recording"] = timer->Recording();
609 oneTimer["StartTime"] = (int)timer->StartTime();
610 oneTimer["StopTime"] = (int)timer->StopTime();
611 oneTimer["File"] = timer->File();
613 cRecordControl* crc = cRecordControls::GetRecordControl(timer);
616 cDevice* crcd = crc->Device();
617 oneTimer["DeviceNumber"] = crcd->DeviceNumber();
621 oneTimer["DeviceNumber"] = Json::Value::null;
624 const cChannel* channel = timer->Channel();
627 oneTimer["ChannelName"] = channel->Name();
631 oneTimer["ChannelName"] = Json::Value::null;
634 jstimers.append(oneTimer);
638 js["CurrentRecordings"] = jstimers;
645 bool VDRClient::timerset(PFMap& postFields, Json::Value& js) // RETHROWS
647 logger->debug("timerset");
649 std::string sTimerString = getVarString(postFields, "timerstring");
651 logger->debug("timerset: '{}'", sTimerString);
652 cTimer *timer = new cTimer;
653 if (!timer->Parse(sTimerString.c_str()))
656 js["Result"] = false;
657 js["Error"] = "Failed to parse timer request details";
661 cTimer *t = Timers.GetTimer(timer);
665 js["Result"] = false;
666 js["Error"] = "Timer already exists";
671 Timers.SetModified();
676 bool VDRClient::recinfo(PFMap& postFields, Json::Value& js) // RETHROWS
678 logger->debug("recinfo");
680 std::string reqfilename = getVarString(postFields, "filename");
681 logger->debug("recinfo: {}", reqfilename);
683 cRecordings Recordings;
684 Recordings.Load(); // probably have to do this
685 cRecording *recording = Recordings.GetByName(reqfilename.c_str());
689 logger->error("recinfo: recinfo found no recording");
690 js["Result"] = false;
694 js["IsNew"] = recording->IsNew();
695 js["LengthInSeconds"] = recording->LengthInSeconds();
696 js["FileSizeMB"] = recording->FileSizeMB();
697 js["Name"] = recording->Name() ? recording->Name() : Json::Value::null;
698 js["Priority"] = recording->Priority();
699 js["LifeTime"] = recording->Lifetime();
700 js["Start"] = (Json::UInt)recording->Start();
702 js["CurrentlyRecordingStart"] = 0;
703 js["CurrentlyRecordingStop"] = 0;
704 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
707 js["CurrentlyRecordingStart"] = (Json::UInt)rc->Timer()->StartTime();
708 js["CurrentlyRecordingStop"] = (Json::UInt)rc->Timer()->StopTime();
711 js["ResumePoint"] = 0;
713 const cRecordingInfo *info = recording->Info();
716 js["ChannelName"] = info->ChannelName() ? info->ChannelName() : Json::Value::null;
717 js["Title"] = info->Title() ? info->Title() : Json::Value::null;
718 js["ShortText"] = info->ShortText() ? info->ShortText() : Json::Value::null;
719 js["Description"] = info->Description() ? info->Description() : Json::Value::null;
721 const cComponents* components = info->Components();
724 js["Components"] = Json::Value::null;
728 Json::Value jscomponents;
730 tComponent* component;
731 for (int i = 0; i < components->NumComponents(); i++)
733 component = components->Component(i);
735 Json::Value oneComponent;
736 oneComponent["Stream"] = component->stream;
737 oneComponent["Type"] = component->type;
738 oneComponent["Language"] = component->language ? component->language : Json::Value::null;
739 oneComponent["Description"] = component->description ? component->description : Json::Value::null;
740 jscomponents.append(oneComponent);
743 js["Components"] = jscomponents;
746 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
747 if (ResumeFile.Read() >= 0) js["ResumePoint"] = floor(ResumeFile.Read() / info->FramesPerSecond());
754 bool VDRClient::recstop(PFMap& postFields, Json::Value& js) // RETHROWS
756 logger->debug("recstop");
758 std::string reqfilename = getVarString(postFields, "filename");
759 logger->debug("recstop: {}", reqfilename);
761 cRecordings Recordings;
762 Recordings.Load(); // probably have to do this
763 cRecording *recording = Recordings.GetByName(reqfilename.c_str());
767 logger->error("recstop: recstop found no recording");
768 js["Result"] = false;
772 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
775 logger->error("recstop: not currently recording");
776 js["Result"] = false;
780 if (Timers.BeingEdited())
782 logger->debug("recstop: timers being edited elsewhere");
783 js["Result"] = false;
787 cTimer* timer = rc->Timer();
790 logger->error("recstop: timer not found");
791 js["Result"] = false;
795 timer->ClrFlags(tfActive);
796 Timers.SetModified();
803 bool VDRClient::recdel(PFMap& postFields, Json::Value& js) // RETHROWS
805 logger->debug("recdel");
807 std::string reqfilename = getVarString(postFields, "filename");
808 logger->debug("recdel: {}", reqfilename);
810 cRecordings Recordings;
811 Recordings.Load(); // probably have to do this
812 cRecording *recording = Recordings.GetByName(reqfilename.c_str());
816 js["Result"] = false;
817 js["Error"] = "Could not find recording to delete";
821 logger->debug("recdel: Deleting recording: {}", recording->Name());
822 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
825 js["Result"] = false;
826 js["Error"] = "This recording is still recording.. ho ho";
830 if (recording->Delete())
832 ::Recordings.DelByName(recording->FileName());
837 js["Result"] = false;
838 js["Error"] = "Failed to delete recording";
844 void VDRClient::pathsForRecordingName(const std::string& recordingName,
845 std::string& dirNameSingleDate,
846 std::string& dirNameSingleTitle,
847 std::string& dirNameSingleFolder,
848 std::string& dirNameFullPathTitle,
849 std::string& dirNameFullPathDate) // throws int
851 cRecordings Recordings;
853 cRecording* recordingObj = Recordings.GetByName(recordingName.c_str());
854 if (!recordingObj) throw 2;
856 cRecordControl *rc = cRecordControls::GetRecordControl(recordingObj->FileName());
859 logger->debug("paths: recording: {}", recordingObj->Name());
860 logger->debug("paths: recording: {}", recordingObj->FileName());
862 const char* t = recordingObj->FileName();
863 dirNameFullPathDate = t;
867 // Find the datedirname
868 for(k = strlen(t) - 1; k >= 0; k--)
872 logger->debug("recmoverename: l1: {}", strlen(&t[k+1]) + 1);
873 dirNameSingleDate.assign(&t[k+1], strlen(t) - k - 1);
874 logger->debug("paths: dirNameSingleDate: '{}'", dirNameSingleDate);
879 // Find the titledirname
881 for(j = k-1; j >= 0; j--)
885 logger->debug("recmoverename: l2: {}", k - j);
886 dirNameSingleTitle.assign(&t[j+1], k - j - 1);
887 logger->debug("paths: dirNameSingleTitle: '{}'", dirNameSingleTitle);
892 // Find the foldername
894 const char* vidDirStr = cVideoDirectory::Name();
895 int vidDirStrLen = strlen(vidDirStr);
897 logger->debug("recmoverename: j = {}, strlenvd = {}", j, vidDirStrLen);
898 if (j > vidDirStrLen) // Rec is in a subfolder now
900 for(m = j-1; m >= 0; m--)
904 logger->debug("recmoverename: l3: {}", j - m);
905 dirNameSingleFolder.assign(&t[m+1], j - m - 1);
906 logger->debug("paths: dirNameSingleFolder: '{}'", dirNameSingleFolder);
912 dirNameFullPathTitle.assign(t, k);
913 logger->debug("paths: dirNameFullPathTitle: '{}'", dirNameFullPathTitle);
916 bool VDRClient::recrename(PFMap& postFields, Json::Value& js) // RETHROWS
918 logger->debug("recrename");
920 std::string fileNameToAffect = getVarString(postFields, "filename");
921 std::string requestedNewStr = getVarString(postFields, "newname");
923 std::string dirNameSingleDate;
924 std::string dirNameSingleTitle;
925 std::string dirNameSingleFolder;
926 std::string dirNameFullPathTitle;
927 std::string dirNameFullPathDate;
931 pathsForRecordingName(fileNameToAffect, dirNameSingleDate,
932 dirNameSingleTitle, dirNameSingleFolder,
933 dirNameFullPathTitle, dirNameFullPathDate);
935 char* requestedNewSingleTitle = (char*)malloc(requestedNewStr.size() + 1);
936 strcpy(requestedNewSingleTitle, requestedNewStr.c_str());
937 logger->debug("recmoverename: to: {}", requestedNewSingleTitle);
939 requestedNewSingleTitle = ExchangeChars(requestedNewSingleTitle, true);
940 if (!strlen(requestedNewSingleTitle)) throw 9;
941 logger->debug("recmoverename: EC: {}", requestedNewSingleTitle);
943 const char* vidDirStr = cVideoDirectory::Name();
944 logger->debug("recmoverename: viddir: {}", vidDirStr);
946 // Could be a new path - construct that first and test
948 std::string newDirNameFullPathTitle = vidDirStr;
949 newDirNameFullPathTitle.append("/");
951 if (!dirNameSingleFolder.empty())
953 newDirNameFullPathTitle.append(dirNameSingleFolder);
954 newDirNameFullPathTitle.append("/");
957 newDirNameFullPathTitle.append(requestedNewSingleTitle);
958 free(requestedNewSingleTitle);
960 logger->debug("recrename: NPT2: {}", newDirNameFullPathTitle);
963 int statret = stat(newDirNameFullPathTitle.c_str(), &dstat);
964 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
966 logger->debug("recrename: new path does not exist (1)");
967 int mkdirret = mkdir(newDirNameFullPathTitle.c_str(), 0755);
968 if (mkdirret != 0) throw 4;
970 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR)))
972 // Something exists but it's not a dir
976 // New path now created or was there already
978 std::string newDirNameFullPathDate = newDirNameFullPathTitle + "/";
979 newDirNameFullPathDate.append(dirNameSingleDate);
981 logger->debug("recrename: doing rename '{}' '{}'", dirNameFullPathDate, newDirNameFullPathDate);
982 if (rename(dirNameFullPathDate.c_str(), newDirNameFullPathDate.c_str()) != 0) throw 8;
984 // Success. Test for remove old dir containter
985 rmdir(dirNameFullPathTitle.c_str()); // can't do anything about a fail result at this point.
987 ::Recordings.Update();
989 js["NewRecordingFileName"] = newDirNameFullPathDate;
993 js["Result"] = false;
996 logger->error("recrename: Bad parameters");
997 js["Error"] = "Bad request parameters";
1001 logger->error("recrename: Could not find recording to move");
1002 js["Error"] = "Bad filename";
1006 logger->error("recrename: Could not move recording, it is still recording");
1007 js["Error"] = "Cannot move recording in progress";
1011 logger->error("recrename: Failed to make new dir (1)");
1012 js["Error"] = "Failed to create new directory (1)";
1016 logger->error("recrename: Something already exists? (1)");
1017 js["Error"] = "Something already exists at the new path (1)";
1021 logger->error("recrename: Rename failed");
1022 js["Error"] = "Rename failed";
1026 logger->error("recrename: ExchangeChars lost our string");
1027 js["Error"] = "Rename failed";
1034 bool VDRClient::recmove(PFMap& postFields, Json::Value& js) // RETHROWS
1036 logger->debug("recmove");
1038 std::string fileNameToAffect = getVarString(postFields, "filename");
1039 std::string requestedNewStr = getVarString(postFields, "newpath");
1041 std::string dirNameSingleDate;
1042 std::string dirNameSingleTitle;
1043 std::string dirNameSingleFolder;
1044 std::string dirNameFullPathTitle;
1045 std::string dirNameFullPathDate;
1049 pathsForRecordingName(fileNameToAffect, dirNameSingleDate,
1050 dirNameSingleTitle, dirNameSingleFolder,
1051 dirNameFullPathTitle, dirNameFullPathDate);
1053 char* requestedNewSinglePath = (char*)malloc(requestedNewStr.size() + 1);
1054 strcpy(requestedNewSinglePath, requestedNewStr.c_str());
1055 logger->debug("recmoverename: to: {}", requestedNewSinglePath);
1057 requestedNewSinglePath = ExchangeChars(requestedNewSinglePath, true);
1058 if (!strlen(requestedNewSinglePath)) throw 9;
1059 logger->debug("recmoverename: EC: {}", requestedNewSinglePath);
1061 const char* vidDirStr = cVideoDirectory::Name();
1062 logger->debug("recmoverename: viddir: {}", vidDirStr);
1064 // Could be a new path - construct that first and test
1066 std::string newDirNameFullPathTitle = vidDirStr;
1067 newDirNameFullPathTitle.append(requestedNewSinglePath);
1068 free(requestedNewSinglePath);
1070 logger->debug("recmove: NPT: {}", newDirNameFullPathTitle);
1073 int statret = stat(newDirNameFullPathTitle.c_str(), &dstat);
1074 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
1076 logger->debug("recmove: new path does not exist (1)");
1077 int mkdirret = mkdir(newDirNameFullPathTitle.c_str(), 0755);
1078 if (mkdirret != 0) throw 4;
1080 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR)))
1082 // Something exists but it's not a dir
1086 // New path now created or was there already
1088 newDirNameFullPathTitle.append(dirNameSingleTitle);
1089 logger->debug("recmove: {}", newDirNameFullPathTitle);
1091 statret = stat(newDirNameFullPathTitle.c_str(), &dstat);
1092 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
1094 logger->debug("recmove: new dir does not exist (2)");
1095 int mkdirret = mkdir(newDirNameFullPathTitle.c_str(), 0755);
1096 if (mkdirret != 0) throw 6;
1098 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR)))
1100 // Something exists but it's not a dir
1104 // Ok, the directory container has been made, or it pre-existed.
1106 std::string newDirNameFullPathDate = newDirNameFullPathTitle + "/";
1107 newDirNameFullPathDate.append(dirNameSingleDate);
1109 logger->debug("recmove: doing rename '{}' '{}'", dirNameFullPathDate, newDirNameFullPathDate);
1110 if (rename(dirNameFullPathDate.c_str(), newDirNameFullPathDate.c_str()) != 0) throw 8;
1112 // Success. Test for remove old dir containter
1113 rmdir(dirNameFullPathTitle.c_str()); // can't do anything about a fail result at this point.
1115 // Test for remove old foldername
1116 if (!dirNameSingleFolder.empty())
1118 std::string dirNameFullPathFolder = vidDirStr;
1119 dirNameFullPathFolder.append("/");
1120 dirNameFullPathFolder.append(dirNameSingleFolder);
1122 logger->debug("recmove: oldfoldername: {}", dirNameFullPathFolder);
1125 rmdir() deletes a directory, which must be empty.
1126 ENOTEMPTY - pathname contains entries other than . and ..
1127 So, should be safe to call rmdir on non-empty dir
1129 rmdir(dirNameFullPathFolder.c_str()); // can't do anything about a fail result at this point.
1132 ::Recordings.Update();
1133 js["Result"] = true;
1134 js["NewRecordingFileName"] = newDirNameFullPathDate;
1138 js["Result"] = false;
1141 logger->error("recmove: Bad parameters");
1142 js["Error"] = "Bad request parameters";
1146 logger->error("recmove: Could not find recording to move");
1147 js["Error"] = "Bad filename";
1151 logger->error("recmove: Could not move recording, it is still recording");
1152 js["Error"] = "Cannot move recording in progress";
1156 logger->error("recmove: Failed to make new dir (1)");
1157 js["Error"] = "Failed to create new directory (1)";
1161 logger->error("recmove: Something already exists? (1)");
1162 js["Error"] = "Something already exists at the new path (1)";
1166 logger->error("recmove: Failed to make new dir (2)");
1167 js["Error"] = "Failed to create new directory (2)";
1171 logger->error("recmove: Something already exists?");
1172 js["Error"] = "Something already exists at the new path";
1176 logger->error("recmove: Rename failed");
1177 js["Error"] = "Move failed";
1181 logger->error("recrename: ExchangeChars lost our string");
1182 js["Error"] = "Rename failed";
1189 bool VDRClient::timersetactive(PFMap& postFields, Json::Value& js) // RETHROWS
1191 logger->debug("timersetactive");
1193 std::string rChannelID = getVarString(postFields, "ChannelID");
1194 std::string rName = getVarString(postFields, "Name");
1195 std::string rStartTime = getVarString(postFields, "StartTime");
1196 std::string rStopTime = getVarString(postFields, "StopTime");
1197 std::string rWeekDays = getVarString(postFields, "WeekDays");
1198 std::string tNewActive = getVarString(postFields, "SetActive");
1200 logger->debug("timersetactive: {} {}:{}:{}:{}:{}", tNewActive, rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1202 cTimer* timer = findTimer(rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
1205 if (tNewActive == "true") timer->SetFlags(tfActive);
1206 else if (tNewActive == "false") timer->ClrFlags(tfActive);
1209 js["Result"] = false;
1210 js["Error"] = "Bad request parameters";
1214 js["Result"] = true;
1215 Timers.SetModified();
1219 js["Result"] = false;
1220 js["Error"] = "Timer not found";
1224 bool VDRClient::timerdel(PFMap& postFields, Json::Value& js) // RETHROWS
1226 logger->debug("timerdel");
1228 std::string rChannelID = getVarString(postFields, "ChannelID");
1229 std::string rName = getVarString(postFields, "Name");
1230 std::string rStartTime = getVarString(postFields, "StartTime");
1231 std::string rStopTime = getVarString(postFields, "StopTime");
1232 std::string rWeekDays = getVarString(postFields, "WeekDays");
1234 logger->debug("timerdel: {}:{}:{}:{}:{}", rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1236 if (Timers.BeingEdited())
1238 logger->debug("timerdel: Unable to delete timer - timers being edited at VDR");
1239 js["Result"] = false;
1240 js["Error"] = "Timers being edited at VDR";
1244 cTimer* timer = findTimer(rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
1247 if (timer->Recording())
1249 logger->debug("timerdel: Unable to delete timer - timer is running");
1250 js["Result"] = false;
1251 js["Error"] = "Timer is running";
1256 Timers.SetModified();
1257 js["Result"] = true;
1261 js["Result"] = false;
1262 js["Error"] = "Timer not found";
1266 bool VDRClient::timerisrecording(PFMap& postFields, Json::Value& js) // RETHROWS
1268 logger->debug("timerisrecording");
1270 std::string rChannelID = getVarString(postFields, "ChannelID");
1271 std::string rName = getVarString(postFields, "Name");
1272 std::string rStartTime = getVarString(postFields, "StartTime");
1273 std::string rStopTime = getVarString(postFields, "StopTime");
1274 std::string rWeekDays = getVarString(postFields, "WeekDays");
1276 logger->debug("timerisrecording: {}:{}:{}:{}:{}", rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1278 cTimer* timer = findTimer(rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
1281 js["Recording"] = timer->Recording();
1282 js["Pending"] = timer->Pending();
1283 js["Result"] = true;
1287 js["Result"] = false;
1288 js["Error"] = "Timer not found";
1292 bool VDRClient::recresetresume(PFMap& postFields, Json::Value& js) // RETHROWS
1294 logger->debug("recresetresume");
1296 std::string reqfilename = getVarString(postFields, "filename");
1297 logger->debug("recresetresume: {}", reqfilename);
1299 cRecordings Recordings;
1300 Recordings.Load(); // probably have to do this
1301 cRecording *recording = Recordings.GetByName(reqfilename.c_str());
1305 js["Result"] = false;
1306 js["Error"] = "Could not find recording to reset resume";
1310 logger->debug("recresetresume: Reset resume for: {}", recording->Name());
1312 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
1313 if (ResumeFile.Read() >= 0)
1315 ResumeFile.Delete();
1316 js["Result"] = true;
1321 js["Result"] = false;
1322 js["Error"] = "Recording has no resume point";
1327 bool VDRClient::timeredit(PFMap& postFields, Json::Value& js) // RETHROWS
1329 logger->debug("timeredit");
1331 std::string oldName = getVarString(postFields, "OldName");
1332 std::string oldActive = getVarString(postFields, "OldActive");
1333 std::string oldChannelID = getVarString(postFields, "OldChannelID");
1334 std::string oldDay = getVarString(postFields, "OldDay");
1335 std::string oldWeekDays = getVarString(postFields, "OldWeekDays");
1336 std::string oldStartTime = getVarString(postFields, "OldStartTime");
1337 std::string oldStopTime = getVarString(postFields, "OldStopTime");
1338 std::string oldPriority = getVarString(postFields, "OldPriority");
1339 std::string oldLifetime = getVarString(postFields, "OldLifetime");
1341 logger->debug("timeredit: {} {} {} {} {} {} {} {} {}", oldName, oldActive, oldChannelID, oldDay, oldWeekDays, oldStartTime, oldStopTime, oldPriority, oldLifetime);
1343 std::string newName = getVarString(postFields, "NewName");
1344 std::string newActive = getVarString(postFields, "NewActive");
1345 std::string newChannelID = getVarString(postFields, "NewChannelID");
1346 std::string newDay = getVarString(postFields, "NewDay");
1347 std::string newWeekDays = getVarString(postFields, "NewWeekDays");
1348 std::string newStartTime = getVarString(postFields, "NewStartTime");
1349 std::string newStopTime = getVarString(postFields, "NewStopTime");
1350 std::string newPriority = getVarString(postFields, "NewPriority");
1351 std::string newLifetime = getVarString(postFields, "NewLifetime");
1353 logger->debug("timeredit: {} {} {} {} {} {} {} {} {}", newName, newActive, newChannelID, newDay, newWeekDays, newStartTime, newStopTime, newPriority, newLifetime);
1355 cTimer* timer = findTimer2(oldName.c_str(), oldActive.c_str(), oldChannelID.c_str(), oldDay.c_str(), oldWeekDays.c_str(), oldStartTime.c_str(), oldStopTime.c_str(), oldPriority.c_str(), oldLifetime.c_str());
1358 js["Result"] = false;
1359 js["Error"] = "Timer not found";
1363 // Old version commented below (now removed) used to set each thing individually based on whether it had changed. However, since
1364 // the only way to change the timer channel appears to be with the cTimer::Parse function, might as well use that
1365 // for everything it supports
1366 // Except flags. Get current flags, set using Parse, then add/remove active as needed the other way.
1368 time_t nstt = std::stoi(newStartTime);
1370 localtime_r(&nstt, &nstm);
1371 int nssf = (nstm.tm_hour * 100) + nstm.tm_min;
1373 time_t nztt = std::stoi(newStopTime);
1375 localtime_r(&nztt, &nztm);
1376 int nzsf = (nztm.tm_hour * 100) + nztm.tm_min;
1378 std::replace(newName.begin(), newName.end(), ':', '|');
1380 // ? Convert to std::string?
1381 cString parseBuffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s",
1382 timer->Flags(), newChannelID.c_str(), *(cTimer::PrintDay(std::stoi(newDay), std::stoi(newWeekDays), true)),
1383 nssf, nzsf, std::stoi(newPriority), std::stoi(newLifetime), newName.c_str(), timer->Aux() ? timer->Aux() : "");
1385 logger->debug("timeredit: new parse: {}", *parseBuffer);
1387 bool parseResult = timer->Parse(*parseBuffer);
1390 js["Result"] = false;
1391 js["Error"] = "Timer parsing failed";
1395 if (timer->HasFlags(tfActive) != !(strcasecmp(newActive.c_str(), "true")))
1397 logger->debug("timeredit: {} {} set new active: {}", timer->HasFlags(tfActive), !(strcasecmp(newActive.c_str(), "true")), newActive);
1399 if (strcasecmp(newActive.c_str(), "true") == 0)
1401 timer->SetFlags(tfActive);
1403 else if (strcasecmp(newActive.c_str(), "false") == 0)
1405 timer->ClrFlags(tfActive);
1409 js["Result"] = false;
1410 js["Error"] = "Bad request parameters";
1415 js["Result"] = true;
1416 Timers.SetModified();
1420 bool VDRClient::epgfilteradd(PFMap& postFields, Json::Value& js) // RETHROWS
1422 std::string channel = getVarString(postFields, "channel");
1423 std::string programme = getVarString(postFields, "programme");
1425 logger->debug("epgFilterAdd: {} {}", channel, programme);
1427 libconfig::Config epgFilter;
1428 if (!loadEpgFilter(epgFilter))
1430 js["Result"] = false;
1431 js["Error"] = "Error initialising EPG filter";
1435 libconfig::Setting& setting = epgFilter.lookup("filters");
1436 libconfig::Setting& newPair = setting.add(libconfig::Setting::Type::TypeGroup);
1437 libconfig::Setting& newChannel = newPair.add("c", libconfig::Setting::Type::TypeString);
1438 newChannel = channel;
1439 libconfig::Setting& newProgramme = newPair.add("p", libconfig::Setting::Type::TypeString);
1440 newProgramme = programme;
1442 if (!saveEpgFilter(epgFilter))
1444 js["Result"] = false;
1445 js["Error"] = "Failed to save EPG filter";
1449 js["Result"] = true;
1453 bool VDRClient::epgfilterdel(PFMap& postFields, Json::Value& js) // RETHROWS
1455 std::string channel = getVarString(postFields, "channel");
1456 std::string programme = getVarString(postFields, "programme");
1458 logger->debug("epgFilterDel: {} {}", channel, programme);
1460 libconfig::Config epgFilter;
1461 if (!loadEpgFilter(epgFilter))
1463 js["Result"] = false;
1464 js["Error"] = "Error initialising EPG filter";
1470 libconfig::Setting& setting = epgFilter.lookup("filters");
1471 int numFilters = setting.getLength();
1473 for (x = 0; x < numFilters; x++)
1475 libconfig::Setting& pair = setting[x];
1478 if (!pair.lookupValue("c", c) || !pair.lookupValue("p", p))
1480 js["Result"] = false;
1481 js["Error"] = "Filter file format error";
1484 logger->debug("loop: {} {}", c, p);
1485 if ((c == channel) && (p == programme))
1489 if (!saveEpgFilter(epgFilter))
1491 js["Result"] = false;
1492 js["Error"] = "Failed to save EPG filter";
1496 logger->debug("Found and deleted: {} {}", c, p);
1497 js["Result"] = true;
1502 js["Result"] = false;
1503 js["Error"] = "Channel/Programme not found";
1506 catch (std::exception e)
1508 js["Result"] = false;
1509 js["Error"] = "Unknown error";
1514 bool VDRClient::epgfilterget(PFMap& postFields, Json::Value& js) // RETHROWS
1516 logger->debug("epgFilterget");
1518 libconfig::Config epgFilter;
1519 if (!loadEpgFilter(epgFilter))
1521 js["Result"] = false;
1522 js["Error"] = "Error initialising EPG filter";
1528 libconfig::Setting& setting = epgFilter.lookup("filters");
1529 int numFilters = setting.getLength();
1531 Json::Value jsfilters(Json::arrayValue);
1532 for (x = 0; x < numFilters; x++)
1534 libconfig::Setting& pair = setting[x];
1537 if (!pair.lookupValue("c", c) || !pair.lookupValue("p", p))
1539 js["Result"] = false;
1540 js["Error"] = "Filter file format error";
1543 logger->debug("loop: {} {}", c, p);
1544 Json::Value oneFilter;
1547 jsfilters.append(oneFilter);
1549 js["EPGFilters"] = jsfilters;
1551 catch (std::exception e)
1553 js["Result"] = false;
1554 js["Error"] = "Unknown error";
1558 js["Result"] = true;
1562 //////////////////////////////////////////////////////////////////////////////////////////////////
1564 const cEvent* VDRClient::getEvent(Json::Value& js, int channelNumber, int eventID, int aroundTime)
1566 cChannel* channel = NULL;
1567 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
1569 if (channel->GroupSep()) continue;
1570 if (channel->Number() == channelNumber) break;
1575 logger->error("getevent: Could not find requested channel: {}", channelNumber);
1576 js["Error"] = "Could not find channel";
1580 cSchedulesLock MutexLock;
1581 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1584 logger->error("getevent: Could not find requested channel: {}", channelNumber);
1585 js["Error"] = "Internal schedules error (1)";
1589 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1592 logger->error("getevent: Could not find requested channel: {}", channelNumber);
1593 js["Error"] = "Internal schedules error (2)";
1597 const cEvent* event = NULL;
1599 event = Schedule->GetEvent(eventID);
1601 event = Schedule->GetEventAround(aroundTime);
1605 logger->error("getevent: Could not find requested event: {}", eventID);
1606 js["Error"] = "Internal schedules error (3)";
1613 cTimer* VDRClient::findTimer(const char* rChannelID, const char* rName, const char* rStartTime, const char* rStopTime, const char* rWeekDays)
1615 int numTimers = Timers.Count();
1617 for (int i = 0; i < numTimers; i++)
1619 timer = Timers.Get(i);
1621 logger->debug("findtimer: current: {}", (const char*)timer->ToText(true));
1622 logger->debug("findtimer: {}", (const char*)timer->Channel()->GetChannelID().ToString());
1623 logger->debug("findtimer: {}", rChannelID);
1624 logger->debug("findtimer: {}", timer->File());
1625 logger->debug("findtimer: {}", rName);
1626 logger->debug("findtimer: {}", timer->StartTime());
1627 logger->debug("findtimer: {}", rStartTime);
1628 logger->debug("findtimer: {}", timer->StopTime());
1629 logger->debug("findtimer: {}", rStopTime);
1630 logger->debug("findtimer: {}", timer->WeekDays());
1631 logger->debug("findtimer: {}", rWeekDays);
1634 (strcmp(timer->Channel()->GetChannelID().ToString(), rChannelID) == 0)
1635 && (strcmp(timer->File(), rName) == 0)
1636 && (timer->StartTime() == atoi(rStartTime))
1637 && (timer->StopTime() == atoi(rStopTime))
1638 && (timer->WeekDays() == atoi(rWeekDays))
1641 logger->debug("findtimer: found");
1645 logger->debug("findtimer: no timer found");
1649 cTimer* VDRClient::findTimer2(const char* rName, const char* rActive, const char* rChannelID, const char* rDay, const char* rWeekDays, const char* rStartTime, const char* rStopTime, const char* rPriority, const char* rLifetime)
1651 int numTimers = Timers.Count();
1653 logger->debug("findtimer2: {} {} {} {} {} {} {} {} {}", rName, rActive, rChannelID, rDay, rWeekDays, rStartTime, rStopTime, rPriority, rLifetime);
1654 for (int i = 0; i < numTimers; i++)
1656 timer = Timers.Get(i);
1658 logger->debug("findtimer2: search: {} {} {} {} {} {} {} {} {}", timer->File(), timer->HasFlags(tfActive), (const char*)timer->Channel()->GetChannelID().ToString(),
1659 (int)timer->Day(), timer->WeekDays(), (int)timer->StartTime(), (int)timer->StopTime(),
1660 timer->Priority(), timer->Lifetime());
1662 if ( (strcmp(timer->File(), rName) == 0)
1663 && (timer->HasFlags(tfActive) == !(strcasecmp(rActive, "true")))
1664 && (strcmp(timer->Channel()->GetChannelID().ToString(), rChannelID) == 0)
1665 && (timer->Day() == atoi(rDay))
1666 && (timer->WeekDays() == atoi(rWeekDays))
1667 && (timer->StartTime() == atoi(rStartTime))
1668 && (timer->StopTime() == atoi(rStopTime))
1669 && (timer->Priority() == atoi(rPriority))
1670 && (timer->Lifetime() == atoi(rLifetime))
1673 logger->debug("findtimer2: found");
1677 logger->debug("findtimer2: no timer found");
1681 int VDRClient::getVarInt(PFMap& postFields, const char* paramName) // THROWS
1683 auto i = postFields.find(paramName);
1684 if (i == postFields.end()) throw BadParamException(paramName);
1686 try { r = std::stoi(i->second); }
1687 catch (std::exception e) { throw BadParamException(paramName); }
1691 std::string VDRClient::getVarString(PFMap& postFields, const char* paramName) // THROWS
1693 auto i = postFields.find(paramName);
1694 if (i == postFields.end()) throw BadParamException(paramName);
1695 if (i->second.empty()) throw BadParamException(paramName);
1699 bool VDRClient::loadEpgFilter(libconfig::Config& epgFilter)
1701 const char* configDir = cPlugin::ConfigDirectory("jsonserver");
1704 logger->error("loadEpgFilter: Error: Could not get config dir from VDR");
1708 std::string epgFilterFile(std::string(configDir) + std::string("/epgfilter.conf"));
1709 FILE* fp = fopen(epgFilterFile.c_str(), "a");
1712 logger->error("loadEpgFilter: Error: Failed to fopen epgfilter.conf");
1719 epgFilter.readFile(epgFilterFile.c_str());
1721 catch (const libconfig::FileIOException &fioex)
1723 logger->error("loadEpgFilter: Error: Failed to read filter file");
1726 catch(const libconfig::ParseException &pex)
1728 logger->error("loadEpgFilter: Parse error at {}: {} - {}", pex.getFile(), pex.getLine(), pex.getError());
1734 libconfig::Setting& setting = epgFilter.lookup("filters");
1736 if (!setting.isList())
1738 logger->error("loadEpgFilter: Error: Failed to read filter file (2)");
1742 catch (libconfig::SettingNotFoundException e)
1744 libconfig::Setting& setting = epgFilter.getRoot();
1745 setting.add("filters", libconfig::Setting::Type::TypeList);
1751 bool VDRClient::saveEpgFilter(libconfig::Config& epgFilter)
1753 const char* configDir = cPlugin::ConfigDirectory("jsonserver");
1756 logger->error("saveEpgFilter: Error: Could not get config dir from VDR");
1760 std::string epgFilterFile(std::string(configDir) + std::string("/epgfilter.conf"));
1763 epgFilter.writeFile(epgFilterFile.c_str());
1764 logger->debug("saveEpgFilter: EPG filter saved");
1767 catch (const libconfig::FileIOException& e)
1769 logger->error("saveEpgFilter: Error: File write error");