3 // Log docs: https://github.com/gabime/spdlog
4 #include <spdlog/spdlog.h>
5 namespace spd = spdlog;
18 #include <jsoncpp/json/json.h>
21 #include <vdr/videodir.h>
22 #include <vdr/recording.h>
24 #include <vdr/timers.h>
26 int jsonserver_request_handler(struct mg_connection *conn)
28 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
30 const struct mg_request_info *request_info = mg_get_request_info(conn);
32 if (strcmp(request_info->uri, "/jsonserver")) return 0; // not for us
35 int wvrl = mg_get_var(request_info->query_string, strlen(request_info->query_string), "req", wvrequest, 20);
38 logger->error("request_handler: Could not decode req");
43 if (!strcmp(request_info->request_method, "OPTIONS"))
45 mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n");
46 mg_printf(conn, "%s", "Access-Control-Allow-Origin: *\r\n");
47 mg_printf(conn, "%s", "Content-Type: text/plain\r\n\r\n");
55 if (!strcmp(request_info->request_method, "POST"))
57 const char* contentLength = mg_get_header(conn, "Content-Length");
58 int contentLengthI = atoi(contentLength);
59 logger->debug("request_hander: POST data content length: {}", contentLengthI);
60 if (contentLengthI > 10000)
62 logger->error("request_handler: Length > 10000, rejecting");
66 if (contentLengthI > 0)
68 // FIXME - assume for now that all post data will be small enough to have arrived immediately
69 int bytesRead = mg_read(conn, postData, contentLengthI);
70 if (bytesRead != contentLengthI)
72 logger->error("request_handler: Could not read up to contentLength");
75 postData[contentLengthI] = '\0';
82 if (!strcmp(wvrequest, "gettime")) success = jsonserver_gettime(js);
83 else if (!strcmp(wvrequest, "diskstats")) success = jsonserver_diskstats(js);
84 else if (!strcmp(wvrequest, "reclist")) success = jsonserver_reclist(js);
85 else if (!strcmp(wvrequest, "recinfo")) success = jsonserver_recinfo(js, postData);
86 else if (!strcmp(wvrequest, "recdel")) success = jsonserver_recdel(js, postData);
87 else if (!strcmp(wvrequest, "recmove")) success = jsonserver_recmove(js, postData);
88 else if (!strcmp(wvrequest, "recrename")) success = jsonserver_recrename(js, postData);
89 else if (!strcmp(wvrequest, "recstop")) success = jsonserver_recstop(js, postData);
90 else if (!strcmp(wvrequest, "recresetresume")) success = jsonserver_recresetresume(js, postData);
91 else if (!strcmp(wvrequest, "channellist")) success = jsonserver_channellist(js);
92 else if (!strcmp(wvrequest, "channelschedule")) success = jsonserver_channelschedule(js, postData);
93 else if (!strcmp(wvrequest, "getscheduleevent")) success = jsonserver_getscheduleevent(js, postData);
94 else if (!strcmp(wvrequest, "timerlist")) success = jsonserver_timerlist(js);
95 else if (!strcmp(wvrequest, "timerdel")) success = jsonserver_timerdel(js, postData);
96 else if (!strcmp(wvrequest, "timerset")) success = jsonserver_timerset(js, postData);
97 else if (!strcmp(wvrequest, "timersetactive")) success = jsonserver_timersetactive(js, postData);
98 else if (!strcmp(wvrequest, "timerisrecording")) success = jsonserver_timerisrecording(js, postData);
99 else if (!strcmp(wvrequest, "timeredit")) success = jsonserver_timeredit(js, postData);
100 else if (!strcmp(wvrequest, "tunersstatus")) success = jsonserver_tunersstatus(js, postData);
101 else if (!strcmp(wvrequest, "epgsearchsame")) success = jsonserver_epgsearchsame(js, postData);
102 else if (!strcmp(wvrequest, "epgdownload")) success = jsonserver_epgdownload(js, postData);
103 else if (!strcmp(wvrequest, "epgsearch")) success = jsonserver_epgsearch(js, postData);
105 if (!success) return 0; // the specific handler failed badly
107 // Now js will be filled
109 Json::StyledWriter sw;
110 std::string jsonout = sw.write(js);
111 mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n");
112 mg_printf(conn, "%s", "Content-Type: text/plain\r\n\r\n");
113 mg_write(conn, jsonout.c_str(), jsonout.length());
118 else if (event == MG_EVENT_LOG)
120 if (request_info->status_code == 400) // bad request
122 logger->debug("Mongoose: 400 BAD REQUEST:");
123 logger->debug("Mongoose {}", request_info->request_method);
124 logger->debug("Mongoose {}", request_info->uri);
125 logger->debug("Mongoose {}", request_info->http_version);
126 logger->debug("Mongoose {}", request_info->query_string);
127 logger->debug("Mongoose {}", request_info->log_message);
128 for (int i = 0; i < request_info->num_headers; i++)
130 logger->debug("Mongoose: {}: {}", request_info->http_headers[i].name, request_info->http_headers[i].value);
135 logger->debug("Mongoose {}", request_info->log_message);
136 logger->debug("Mongoose {}", request_info->uri);
141 // other events not handled:
142 // MG_HTTP_ERROR, MG_INIT_SSL
143 // Let mongoose do something with those
151 bool jsonserver_gettime(Json::Value& js)
153 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
154 logger->debug("get_time");
157 gettimeofday(&tv, NULL);
159 js["Time"] = (Json::UInt64)tv.tv_sec;
160 js["MTime"] = (Json::UInt)(tv.tv_usec/1000);
165 bool jsonserver_diskstats(Json::Value& js)
167 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
168 logger->debug("diskstats");
173 #if APIVERSNUM > 20101
174 int Percent = cVideoDirectory::VideoDiskSpace(&FreeMB, &UsedMB);
176 int Percent = VideoDiskSpace(&FreeMB, &UsedMB);
179 js["FreeMiB"] = FreeMB;
180 js["UsedMiB"] = UsedMB;
181 js["Percent"] = Percent;
187 bool jsonserver_reclist(Json::Value& js)
189 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
190 logger->debug("reclist");
192 Json::Value jsrecordings(Json::arrayValue);
193 cRecordings Recordings;
195 for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
198 oneRec["StartTime"] = (Json::UInt)recording->Start();
199 oneRec["Length"] = (Json::UInt)recording->LengthInSeconds();
200 oneRec["IsNew"] = recording->IsNew();
201 oneRec["Name"] = recording->Name();
202 oneRec["Filename"] = recording->FileName();
203 oneRec["FileSizeMB"] = recording->FileSizeMB();
205 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
206 if (rc) oneRec["CurrentlyRecording"] = true;
207 else oneRec["CurrentlyRecording"] = false;
209 jsrecordings.append(oneRec);
211 js["Recordings"] = jsrecordings;
216 bool jsonserver_recinfo(Json::Value& js, const char* postData)
218 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
219 logger->debug("recinfo");
221 char reqfilename[1000];
222 int mgv1 = mg_get_var(postData, strlen(postData), "filename", reqfilename, 1000);
225 logger->error("recinfo: Could not decode filename");
226 js["Result"] = false;
227 js["Error"] = "Could not decode filename";
231 logger->debug("recinfo: {}", reqfilename);
233 cRecordings Recordings;
234 Recordings.Load(); // probably have to do this
235 cRecording *recording = Recordings.GetByName(reqfilename);
239 logger->error("recinfo: recinfo found no recording");
240 js["Result"] = false;
244 js["IsNew"] = recording->IsNew();
245 js["LengthInSeconds"] = recording->LengthInSeconds();
246 js["FileSizeMB"] = recording->FileSizeMB();
247 js["Name"] = recording->Name() ? recording->Name() : Json::Value::null;
248 js["Priority"] = recording->Priority();
249 js["LifeTime"] = recording->Lifetime();
250 js["Start"] = (Json::UInt)recording->Start();
252 js["CurrentlyRecordingStart"] = 0;
253 js["CurrentlyRecordingStop"] = 0;
254 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
257 js["CurrentlyRecordingStart"] = (Json::UInt)rc->Timer()->StartTime();
258 js["CurrentlyRecordingStop"] = (Json::UInt)rc->Timer()->StopTime();
261 js["ResumePoint"] = 0;
263 const cRecordingInfo *info = recording->Info();
266 js["ChannelName"] = info->ChannelName() ? info->ChannelName() : Json::Value::null;
267 js["Title"] = info->Title() ? info->Title() : Json::Value::null;
268 js["ShortText"] = info->ShortText() ? info->ShortText() : Json::Value::null;
269 js["Description"] = info->Description() ? info->Description() : Json::Value::null;
271 const cComponents* components = info->Components();
274 js["Components"] = Json::Value::null;
278 Json::Value jscomponents;
280 tComponent* component;
281 for (int i = 0; i < components->NumComponents(); i++)
283 component = components->Component(i);
285 Json::Value oneComponent;
286 oneComponent["Stream"] = component->stream;
287 oneComponent["Type"] = component->type;
288 oneComponent["Language"] = component->language ? component->language : Json::Value::null;
289 oneComponent["Description"] = component->description ? component->description : Json::Value::null;
290 jscomponents.append(oneComponent);
293 js["Components"] = jscomponents;
296 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
297 if (ResumeFile.Read() >= 0) js["ResumePoint"] = floor(ResumeFile.Read() / info->FramesPerSecond());
304 bool jsonserver_recstop(Json::Value& js, const char* postData)
306 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
307 logger->debug("recstop");
309 char reqfilename[1000];
310 int mgv1 = mg_get_var(postData, strlen(postData), "filename", reqfilename, 1000);
313 logger->error("recstop: Could not decode filename");
314 js["Result"] = false;
315 js["Error"] = "Could not decode filename";
319 logger->debug("recstop: {}", reqfilename);
321 cRecordings Recordings;
322 Recordings.Load(); // probably have to do this
323 cRecording *recording = Recordings.GetByName(reqfilename);
327 logger->error("recstop: recstop found no recording");
328 js["Result"] = false;
332 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
335 logger->error("recstop: not currently recording");
336 js["Result"] = false;
340 if (Timers.BeingEdited())
342 logger->debug("recstop: timers being edited elsewhere");
343 js["Result"] = false;
347 cTimer* timer = rc->Timer();
350 logger->error("recstop: timer not found");
351 js["Result"] = false;
355 timer->ClrFlags(tfActive);
356 Timers.SetModified();
362 bool jsonserver_recdel(Json::Value& js, const char* postData)
364 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
365 logger->debug("recdel");
367 char reqfilename[1000];
368 int mgv1 = mg_get_var(postData, strlen(postData), "filename", reqfilename, 1000);
371 logger->error("recdel: Could not decode filename");
372 js["Result"] = false;
373 js["Error"] = "Could not decode filename";
377 logger->debug("recdel: {}", reqfilename);
379 cRecordings Recordings;
380 Recordings.Load(); // probably have to do this
381 cRecording *recording = Recordings.GetByName(reqfilename);
385 js["Result"] = false;
386 js["Error"] = "Could not find recording to delete";
390 logger->debug("recdel: Deleting recording: {}", recording->Name());
391 cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
394 js["Result"] = false;
395 js["Error"] = "This recording is still recording.. ho ho";
399 if (recording->Delete())
401 ::Recordings.DelByName(recording->FileName());
406 js["Result"] = false;
407 js["Error"] = "Failed to delete recording";
413 bool jsonserver_recmove(Json::Value& js, const char* postData)
415 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
416 logger->debug("recmove");
418 char* fileNameToMove = NULL;
419 char* requestedNewPath = NULL;
420 char* dateDirName = NULL;
421 char* titleDirName = NULL;
422 char* folderName = NULL;
423 char* newContainer = NULL;
428 int postDataLen = strlen(postData)+1;
429 fileNameToMove = new char[postDataLen];
430 int mgv1 = mg_get_var(postData, postDataLen-1, "filename", fileNameToMove, postDataLen);
431 requestedNewPath = new char[postDataLen];
432 int mgv2 = mg_get_var(postData, postDataLen-1, "newpath", requestedNewPath, postDataLen);
434 if ((mgv1 == -1) || (mgv2 == -1) || !strlen(fileNameToMove) || !strlen(requestedNewPath))
436 logger->error("recmove: request mgvs: {} {}", mgv1, mgv2);
440 cRecordings Recordings;
441 Recordings.Load(); // probably have to do this
442 cRecording* recordingObj = Recordings.GetByName(fileNameToMove);
443 if (!recordingObj) throw 2;
445 cRecordControl *rc = cRecordControls::GetRecordControl(recordingObj->FileName());
448 logger->debug("recmove: moving recording: {}", recordingObj->Name());
449 logger->debug("recmove: moving recording: {}", recordingObj->FileName());
450 logger->debug("recmove: to: {}", requestedNewPath);
452 const char* t = recordingObj->FileName();
456 // Find the datedirname
457 for(k = strlen(t) - 1; k >= 0; k--)
461 logger->debug("recmove: l1: {}", strlen(&t[k+1]) + 1);
462 dateDirName = new char[strlen(&t[k+1]) + 1];
463 strcpy(dateDirName, &t[k+1]);
468 // Find the titledirname
470 for(j = k-1; j >= 0; j--)
474 logger->debug("recmove: l2: {}", k - j);
475 titleDirName = new char[k - j];
476 memcpy(titleDirName, &t[j+1], k - j - 1);
477 titleDirName[k - j - 1] = '\0';
482 // Find the foldername
484 #if APIVERSNUM > 20101
485 const char* vidDirStr = cVideoDirectory::Name();
487 const char* vidDirStr = VideoDirectory;
489 int vidDirStrLen = strlen(vidDirStr);
491 logger->debug("recmove: j = {}, strlenvd = {}", j, vidDirStrLen);
492 if (j > vidDirStrLen) // Rec is in a subfolder now
494 for(m = j-1; m >= 0; m--)
498 logger->debug("recmove: l3: {}", j - m);
499 folderName = new char[j - m];
500 memcpy(folderName, &t[m+1], j - m - 1);
501 folderName[j - m - 1] = '\0';
507 ExchangeChars(requestedNewPath, true);
509 logger->debug("recmove: datedirname: {}", dateDirName);
510 logger->debug("recmove: titledirname: {}", titleDirName);
511 logger->debug("recmove: viddir: {}", vidDirStr);
512 if (folderName) logger->debug("recmove: folderName: {}", folderName);
513 logger->debug("recmove: EC: {}", requestedNewPath);
515 // Could be a new path - construct that first and test
516 newContainer = new char[vidDirStrLen + strlen(requestedNewPath) + strlen(titleDirName) + 1];
517 sprintf(newContainer, "%s%s", vidDirStr, requestedNewPath);
518 logger->debug("recmove: NPT: {}", newContainer);
520 int statret = stat(newContainer, &dstat);
521 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
523 logger->debug("recmove: new path does not exist (1)");
524 int mkdirret = mkdir(newContainer, 0755);
525 if (mkdirret != 0) throw 4;
527 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR)))
529 // Something exists but it's not a dir
533 // New path now created or was there already
535 sprintf(newContainer, "%s%s%s", vidDirStr, requestedNewPath, titleDirName);
536 logger->debug("recmove: {}", newContainer);
538 statret = stat(newContainer, &dstat);
539 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
541 logger->debug("recmove: new dir does not exist (2)");
542 int mkdirret = mkdir(newContainer, 0755);
543 if (mkdirret != 0) throw 6;
545 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR)))
547 // Something exists but it's not a dir
551 // Ok, the directory container has been made, or it pre-existed.
553 newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
554 sprintf(newDir, "%s/%s", newContainer, dateDirName);
556 logger->debug("recmove: doing rename '{}' '{}'", t, newDir);
557 if (rename(t, newDir) != 0) throw 8;
559 // Success. Test for remove old dir containter
560 char* tempOldTitleDir = new char[k+1];
561 memcpy(tempOldTitleDir, t, k);
562 tempOldTitleDir[k] = '\0';
563 logger->debug("recmove: len: {}, cp: {}, strlen: {}, oldtitledir: {}", k+1, k, strlen(tempOldTitleDir), tempOldTitleDir);
564 rmdir(tempOldTitleDir); // can't do anything about a fail result at this point.
565 delete[] tempOldTitleDir;
567 // Test for remove old foldername
570 char* tempOldFolderName = new char[j+1];
571 memcpy(tempOldFolderName, t, j);
572 tempOldFolderName[j] = '\0';
573 logger->debug("recmove: len: {}, cp: {}, strlen: {}, oldfoldername: {}", j+1, j, strlen(tempOldFolderName), tempOldFolderName);
576 rmdir() deletes a directory, which must be empty.
577 ENOTEMPTY - pathname contains entries other than . and ..
578 So, should be safe to call rmdir on non-empty dir
580 rmdir(tempOldFolderName); // can't do anything about a fail result at this point.
581 delete[] tempOldFolderName;
584 ::Recordings.Update();
586 js["NewRecordingFileName"] = newDir;
590 js["Result"] = false;
593 logger->error("recmove: Bad parameters");
594 js["Error"] = "Bad request parameters";
598 logger->error("recmove: Could not find recording to move");
599 js["Error"] = "Bad filename";
603 logger->error("recmove: Could not move recording, it is still recording");
604 js["Error"] = "Cannot move recording in progress";
608 logger->error("recmove: Failed to make new dir (1)");
609 js["Error"] = "Failed to create new directory (1)";
613 logger->error("recmove: Something already exists? (1)");
614 js["Error"] = "Something already exists at the new path (1)";
618 logger->error("recmove: Failed to make new dir (2)");
619 js["Error"] = "Failed to create new directory (2)";
623 logger->error("recmove: Something already exists?");
624 js["Error"] = "Something already exists at the new path";
628 logger->error("recmove: Rename failed");
629 js["Error"] = "Move failed";
633 if (fileNameToMove) delete[] fileNameToMove;
634 if (requestedNewPath) delete[] requestedNewPath;
635 if (dateDirName) delete[] dateDirName;
636 if (titleDirName) delete[] titleDirName;
637 if (folderName) delete[] folderName;
638 if (newContainer) delete[] newContainer;
639 if (newDir) delete[] newDir;
644 bool jsonserver_recrename(Json::Value& js, const char* postData)
646 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
647 logger->debug("recrename");
649 char* fileNameToRename = NULL;
650 char* requestedNewName = NULL;
651 char* dateDirName = NULL;
652 char* titleDirName = NULL;
653 char* folderName = NULL;
654 char* newContainer = NULL;
659 int postDataLen = strlen(postData)+1;
660 fileNameToRename = new char[postDataLen];
661 int mgv1 = mg_get_var(postData, postDataLen-1, "filename", fileNameToRename, postDataLen);
662 requestedNewName = new char[postDataLen];
663 int mgv2 = mg_get_var(postData, postDataLen-1, "newname", requestedNewName, postDataLen);
665 if ((mgv1 == -1) || (mgv2 == -1) || !strlen(fileNameToRename) || !strlen(requestedNewName))
667 logger->error("recrename: request mgvs: {} {}", mgv1, mgv2);
671 cRecordings Recordings;
672 Recordings.Load(); // probably have to do this
673 cRecording* recordingObj = Recordings.GetByName(fileNameToRename);
674 if (!recordingObj) throw 2;
676 cRecordControl *rc = cRecordControls::GetRecordControl(recordingObj->FileName());
679 logger->debug("recrename: renaming recording: {}", recordingObj->Name());
680 logger->debug("recrename: renaming recording: {}", recordingObj->FileName());
681 logger->debug("recrename: to: {}", requestedNewName);
683 const char* t = recordingObj->FileName();
687 // Find the datedirname
688 for(k = strlen(t) - 1; k >= 0; k--)
692 logger->debug("recrename: l1: {}", strlen(&t[k+1]) + 1);
693 dateDirName = new char[strlen(&t[k+1]) + 1];
694 strcpy(dateDirName, &t[k+1]);
699 // Find the titledirname
701 for(j = k-1; j >= 0; j--)
705 logger->debug("recrename: l2: {}", k - j);
706 titleDirName = new char[k - j];
707 memcpy(titleDirName, &t[j+1], k - j - 1);
708 titleDirName[k - j - 1] = '\0';
713 // Find the foldername
715 #if APIVERSNUM > 20101
716 const char* vidDirStr = cVideoDirectory::Name();
718 const char* vidDirStr = VideoDirectory;
720 int vidDirStrLen = strlen(vidDirStr);
722 logger->debug("recrename: j = {}, strlenvd = {}", j, vidDirStrLen);
723 if (j > vidDirStrLen) // Rec is in a subfolder now
725 for(m = j-1; m >= 0; m--)
729 logger->debug("recrename: l3: {}", j - m);
730 folderName = new char[j - m];
731 memcpy(folderName, &t[m+1], j - m - 1);
732 folderName[j - m - 1] = '\0';
738 ExchangeChars(requestedNewName, true);
740 logger->debug("recrename: datedirname: {}", dateDirName);
741 logger->debug("recrename: titledirname: {}", titleDirName);
742 logger->debug("recrename: viddir: {}", vidDirStr);
743 if (folderName) logger->debug("recrename: folderName: {}", folderName);
744 logger->debug("recrename: EC: {}", requestedNewName);
746 // Could be a new path - construct that first and test
750 newContainer = new char[vidDirStrLen + 1 + strlen(folderName) + 1 + strlen(requestedNewName) + 1];
751 sprintf(newContainer, "%s/%s/%s", vidDirStr, folderName, requestedNewName);
755 newContainer = new char[vidDirStrLen + 1 + strlen(requestedNewName) + 1];
756 sprintf(newContainer, "%s/%s", vidDirStr, requestedNewName);
758 logger->debug("recrename: NPT: {}", newContainer);
760 int statret = stat(newContainer, &dstat);
761 if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
763 logger->debug("recrename: new path does not exist (1)");
764 int mkdirret = mkdir(newContainer, 0755);
765 if (mkdirret != 0) throw 4;
767 else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR)))
769 // Something exists but it's not a dir
773 // New path now created or was there already
775 newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
776 sprintf(newDir, "%s/%s", newContainer, dateDirName);
778 logger->debug("recrename: doing rename '{}' '{}'", t, newDir);
779 if (rename(t, newDir) != 0) throw 8;
781 // Success. Test for remove old dir containter
782 char* tempOldTitleDir = new char[k+1];
783 memcpy(tempOldTitleDir, t, k);
784 tempOldTitleDir[k] = '\0';
785 logger->debug("recrename: len: {}, cp: {}, strlen: {}, oldtitledir: {}", k+1, k, strlen(tempOldTitleDir), tempOldTitleDir);
786 rmdir(tempOldTitleDir); // can't do anything about a fail result at this point.
787 delete[] tempOldTitleDir;
789 ::Recordings.Update();
791 js["NewRecordingFileName"] = newDir;
795 js["Result"] = false;
798 logger->error("recrename: Bad parameters");
799 js["Error"] = "Bad request parameters";
803 logger->error("recrename: Could not find recording to move");
804 js["Error"] = "Bad filename";
808 logger->error("recrename: Could not move recording, it is still recording");
809 js["Error"] = "Cannot move recording in progress";
813 logger->error("recrename: Failed to make new dir (1)");
814 js["Error"] = "Failed to create new directory (1)";
818 logger->error("recrename: Something already exists? (1)");
819 js["Error"] = "Something already exists at the new path (1)";
823 logger->error("recrename: Rename failed");
824 js["Error"] = "Move failed";
828 if (fileNameToRename) delete[] fileNameToRename;
829 if (requestedNewName) delete[] requestedNewName;
830 if (dateDirName) delete[] dateDirName;
831 if (titleDirName) delete[] titleDirName;
832 if (folderName) delete[] folderName;
833 if (newContainer) delete[] newContainer;
834 if (newDir) delete[] newDir;
839 bool jsonserver_recresetresume(Json::Value& js, const char* postData)
841 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
842 logger->debug("recresetresume");
844 char reqfilename[1000];
845 int mgv1 = mg_get_var(postData, strlen(postData), "filename", reqfilename, 1000);
848 logger->error("recresetresume: Could not decode filename");
849 js["Result"] = false;
850 js["Error"] = "Could not decode filename";
854 logger->debug("recresetresume: {}", reqfilename);
856 cRecordings Recordings;
857 Recordings.Load(); // probably have to do this
858 cRecording *recording = Recordings.GetByName(reqfilename);
862 js["Result"] = false;
863 js["Error"] = "Could not find recording to reset resume";
867 logger->debug("recresetresume: Reset resume for: {}", recording->Name());
869 cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
870 if (ResumeFile.Read() >= 0)
878 js["Result"] = false;
879 js["Error"] = "Recording has no resume point";
884 bool jsonserver_channellist(Json::Value& js)
886 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
887 logger->debug("channellist");
889 Json::Value jschannels(Json::arrayValue);
892 for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
894 if (!channel->GroupSep())
896 // logger->debug("channellist: name: '{}'", channel->Name());
898 // if (channel->Vpid()) type = 1;
899 // else if (channel->Apid(0)) type = 2;
902 Json::Value oneChannel;
903 oneChannel["ID"] = (const char *)channel->GetChannelID().ToString();
904 oneChannel["Number"] = channel->Number();
905 // oneChannel["Type"] = type;
906 oneChannel["Name"] = channel->Name();
907 //#if VDRVERSNUM < 10703
908 // oneChannel["VType"] = 2;
910 // oneChannel["VType"] = channel->Vtype();
912 jschannels.append(oneChannel);
915 js["Channels"] = jschannels;
920 bool jsonserver_channelschedule(Json::Value& js, const char* postData)
922 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
923 logger->debug("channelschedule: '{}'", postData);
925 char sChannelNumber[15]; int mgv1 = mg_get_var(postData, strlen(postData), "channelnumber", sChannelNumber, 15);
926 char sStartTime[15]; int mgv2 = mg_get_var(postData, strlen(postData), "starttime", sStartTime, 15);
927 char sDuration[15]; int mgv3 = mg_get_var(postData, strlen(postData), "duration", sDuration, 15);
929 if ( (mgv1 == -1) || (mgv2 == -1) || (mgv3 == -1) )
931 logger->error("channelschedule: request mgvs: {} {} {}", mgv1, mgv2, mgv3);
932 js["Result"] = false;
933 js["Error"] = "Bad request parameters";
937 int channelNumber = atoi(sChannelNumber);
938 int startTime = atoi(sStartTime);
939 int duration = atoi(sDuration);
941 cChannel* channel = NULL;
942 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
944 if (channel->GroupSep()) continue;
945 if (channel->Number() == channelNumber) break;
950 logger->error("channelschedule: Could not find requested channel: {}", channelNumber);
951 js["Result"] = false;
952 js["Error"] = "Could not find channel";
956 cSchedulesLock MutexLock;
957 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
960 logger->error("channelschedule: Could not find requested channel: {}", channelNumber);
961 js["Result"] = false;
962 js["Error"] = "Internal schedules error (1)";
965 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
968 logger->error("channelschedule: Could not find requested channel: {}", channelNumber);
969 js["Result"] = false;
970 js["Error"] = "Internal schedules error (2)";
974 Json::Value jsevents(Json::arrayValue);
976 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
979 if ((event->StartTime() + event->Duration()) < time(NULL)) continue;
981 if ((event->StartTime() + event->Duration()) <= startTime) continue;
983 if (event->StartTime() >= (startTime + duration)) continue;
985 Json::Value oneEvent;
986 oneEvent["ID"] = event->EventID();
987 oneEvent["Time"] = (Json::UInt)event->StartTime();
988 oneEvent["Duration"] = event->Duration();
989 oneEvent["Title"] = event->Title() ? event->Title() : "";
990 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
991 oneEvent["HasTimer"] = event->HasTimer();
992 //oneEvent["Description"] = event->Description() ? event->Description() : "";
993 jsevents.append(oneEvent);
997 js["Events"] = jsevents;
1001 bool jsonserver_getscheduleevent(Json::Value& js, const char* postData)
1003 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1004 logger->debug("getscheduleevent: '{}'", postData);
1006 char sChannelNumber[15]; int mgv1 = mg_get_var(postData, strlen(postData), "channelnumber", sChannelNumber, 15);
1007 char sEventID[15]; int mgv2 = mg_get_var(postData, strlen(postData), "eventid", sEventID, 15);
1009 if ( (mgv1 == -1) || (mgv2 == -1) )
1011 logger->error("getscheduleevent: request mgvs: {} {}", mgv1, mgv2);
1012 js["Result"] = false;
1013 js["Error"] = "Bad request parameters";
1017 int channelNumber = atoi(sChannelNumber);
1018 int eventID = atoi(sEventID);
1020 const cEvent* event = jsonserver_getEvent(js, channelNumber, eventID, 0);
1023 js["Result"] = false;
1027 Json::Value oneEvent;
1028 oneEvent["ID"] = event->EventID();
1029 oneEvent["Time"] = (Json::UInt)event->StartTime();
1030 oneEvent["Duration"] = event->Duration();
1031 oneEvent["Title"] = event->Title() ? event->Title() : "";
1032 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
1033 oneEvent["Description"] = event->Description() ? event->Description() : "";
1034 oneEvent["HasTimer"] = event->HasTimer();
1035 oneEvent["RunningStatus"] = event->RunningStatus();
1037 js["Result"] = true;
1038 js["Event"] = oneEvent;
1042 const cEvent* jsonserver_getEvent(Json::Value& js, int channelNumber, int eventID, int aroundTime)
1044 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1046 cChannel* channel = NULL;
1047 for (channel = Channels.First(); channel; channel = Channels.Next(channel))
1049 if (channel->GroupSep()) continue;
1050 if (channel->Number() == channelNumber) break;
1055 logger->error("getevent: Could not find requested channel: {}", channelNumber);
1056 js["Error"] = "Could not find channel";
1060 cSchedulesLock MutexLock;
1061 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1064 logger->error("getevent: Could not find requested channel: {}", channelNumber);
1065 js["Error"] = "Internal schedules error (1)";
1069 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1072 logger->error("getevent: Could not find requested channel: {}", channelNumber);
1073 js["Error"] = "Internal schedules error (2)";
1077 const cEvent* event = NULL;
1080 event = Schedule->GetEvent(eventID);
1084 event = Schedule->GetEventAround(aroundTime);
1089 logger->error("getevent: Could not find requested event: {}", eventID);
1090 js["Error"] = "Internal schedules error (3)";
1097 bool jsonserver_timerlist(Json::Value& js)
1099 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1100 logger->debug("timerlist");
1102 Json::Value jstimers(Json::arrayValue);
1105 int numTimers = Timers.Count();
1107 for (int i = 0; i < numTimers; i++)
1109 timer = Timers.Get(i);
1110 Json::Value oneTimer;
1111 oneTimer["Active"] = timer->HasFlags(tfActive);
1112 oneTimer["Recording"] = timer->Recording();
1113 oneTimer["Pending"] = timer->Pending();
1114 oneTimer["Priority"] = timer->Priority();
1115 oneTimer["Lifetime"] = timer->Lifetime();
1116 oneTimer["ChannelNumber"] = timer->Channel()->Number();
1117 oneTimer["ChannelID"] = (const char *)timer->Channel()->GetChannelID().ToString();
1118 oneTimer["StartTime"] = (int)timer->StartTime();
1119 oneTimer["StopTime"] = (int)timer->StopTime();
1120 oneTimer["Day"] = (int)timer->Day();
1121 oneTimer["WeekDays"] = timer->WeekDays();
1122 oneTimer["Name"] = timer->File();
1124 const cEvent* event = timer->Event();
1127 oneTimer["EventID"] = event->EventID();
1131 int channelNumber = timer->Channel()->Number();
1132 int aroundTime = timer->StartTime() + 1;
1133 logger->debug("timerlist: {}", aroundTime);
1135 const cEvent* eventAround = jsonserver_getEvent(js, channelNumber, 0, aroundTime);
1138 oneTimer["EventID"] = eventAround->EventID();
1142 oneTimer["EventID"] = 0;
1146 jstimers.append(oneTimer);
1149 js["Timers"] = jstimers;
1150 js["Result"] = true;
1154 bool jsonserver_timerset(Json::Value& js, const char* postData)
1156 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1157 logger->debug("timerset");
1159 char sTimerString[1024]; int mgv1 = mg_get_var(postData, strlen(postData), "timerstring", sTimerString, 1024);
1163 logger->error("timerset: Could not get timerstring");
1164 js["Result"] = false;
1165 js["Error"] = "Bad request parameters";
1169 logger->debug("timerset: '{}'", sTimerString);
1170 cTimer *timer = new cTimer;
1171 if (!timer->Parse(sTimerString))
1174 js["Result"] = false;
1175 js["Error"] = "Failed to parse timer request details";
1179 cTimer *t = Timers.GetTimer(timer);
1183 js["Result"] = false;
1184 js["Error"] = "Timer already exists";
1189 Timers.SetModified();
1190 js["Result"] = true;
1194 bool jsonserver_timersetactive(Json::Value& js, const char* postData)
1196 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1197 logger->debug("timersetactive");
1199 char rChannelID[50]; int mgv1 = mg_get_var(postData, strlen(postData), "ChannelID", rChannelID, 50);
1200 char rName[1024]; int mgv2 = mg_get_var(postData, strlen(postData), "Name", rName, 1024);
1201 char rStartTime[20]; int mgv3 = mg_get_var(postData, strlen(postData), "StartTime", rStartTime, 20);
1202 char rStopTime[20]; int mgv4 = mg_get_var(postData, strlen(postData), "StopTime", rStopTime, 20);
1203 char rWeekDays[50]; int mgv5 = mg_get_var(postData, strlen(postData), "WeekDays", rWeekDays, 20);
1204 char tNewActive[20]; int mgv6 = mg_get_var(postData, strlen(postData), "SetActive", tNewActive, 20);
1206 if ( (mgv1 == -1) || (mgv2 == -1) || (mgv3 == -1) || (mgv4 == -1) || (mgv5 == -1) || (mgv6 == -1))
1208 logger->error("timersetactive: request mgvs: {} {} {} {} {} {}", mgv1, mgv2, mgv3, mgv4, mgv5, mgv6);
1209 js["Result"] = false;
1210 js["Error"] = "Bad request parameters";
1214 logger->debug("timersetactive: {} {}:{}:{}:{}:{}", tNewActive, rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1216 cTimer* timer = jsonserver_findTimer(rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1219 if (strcmp(tNewActive, "true") == 0)
1221 timer->SetFlags(tfActive);
1223 else if (strcmp(tNewActive, "false") == 0)
1225 timer->ClrFlags(tfActive);
1229 js["Result"] = false;
1230 js["Error"] = "Bad request parameters";
1234 js["Result"] = true;
1236 Timers.SetModified();
1241 js["Result"] = false;
1242 js["Error"] = "Timer not found";
1246 bool jsonserver_timeredit(Json::Value& js, const char* postData)
1248 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1249 logger->debug("timeredit");
1252 char oldName[1024]; int mgv1 = mg_get_var(postData, strlen(postData), "OldName", oldName, 1024);
1253 char oldActive[20]; int mgv2 = mg_get_var(postData, strlen(postData), "OldActive", oldActive, 20);
1254 char oldChannelID[50]; int mgv3 = mg_get_var(postData, strlen(postData), "OldChannelID", oldChannelID, 50);
1255 char oldDay[20]; int mgv4 = mg_get_var(postData, strlen(postData), "OldDay", oldDay, 20);
1256 char oldWeekDays[50]; int mgv5 = mg_get_var(postData, strlen(postData), "OldWeekDays", oldWeekDays, 20);
1257 char oldStartTime[20]; int mgv6 = mg_get_var(postData, strlen(postData), "OldStartTime", oldStartTime, 20);
1258 char oldStopTime[20]; int mgv7 = mg_get_var(postData, strlen(postData), "OldStopTime", oldStopTime, 20);
1259 char oldPriority[20]; int mgv8 = mg_get_var(postData, strlen(postData), "OldPriority", oldPriority, 20);
1260 char oldLifetime[20]; int mgv9 = mg_get_var(postData, strlen(postData), "OldLifetime", oldLifetime, 20);
1262 if ( (mgv1 == -1) || (mgv2 == -1) || (mgv3 == -1) || (mgv4 == -1) || (mgv5 == -1) || (mgv6 == -1) || (mgv7 == -1) || (mgv8 == -1) || (mgv9 == -1))
1264 logger->error("timeredit: request mgvs: {} {} {} {} {} {} {} {} {}", mgv1, mgv2, mgv3, mgv4, mgv5, mgv6, mgv7, mgv8, mgv9);
1265 js["Result"] = false;
1266 js["Error"] = "Bad request parameters";
1270 logger->debug("timeredit: {} {} {} {} {} {} {} {} {}", oldName, oldActive, oldChannelID, oldDay, oldWeekDays, oldStartTime, oldStopTime, oldPriority, oldLifetime);
1274 char newName[1024]; int mgv11 = mg_get_var(postData, strlen(postData), "NewName", newName, 1024);
1275 char newActive[20]; int mgv12 = mg_get_var(postData, strlen(postData), "NewActive", newActive, 20);
1276 char newChannelID[50]; int mgv13 = mg_get_var(postData, strlen(postData), "NewChannelID", newChannelID, 50);
1277 char newDay[20]; int mgv14 = mg_get_var(postData, strlen(postData), "NewDay", newDay, 20);
1278 char newWeekDays[50]; int mgv15 = mg_get_var(postData, strlen(postData), "NewWeekDays", newWeekDays, 20);
1279 char newStartTime[20]; int mgv16 = mg_get_var(postData, strlen(postData), "NewStartTime", newStartTime, 20);
1280 char newStopTime[20]; int mgv17 = mg_get_var(postData, strlen(postData), "NewStopTime", newStopTime, 20);
1281 char newPriority[20]; int mgv18 = mg_get_var(postData, strlen(postData), "NewPriority", newPriority, 20);
1282 char newLifetime[20]; int mgv19 = mg_get_var(postData, strlen(postData), "NewLifetime", newLifetime, 20);
1284 if ( (mgv11 == -1) || (mgv12 == -1) || (mgv13 == -1) || (mgv14 == -1) || (mgv15 == -1) || (mgv16 == -1) || (mgv17 == -1) || (mgv18 == -1) || (mgv19 == -1))
1286 logger->error("timeredit: request mgvs: {} {} {} {} {} {} {} {} {}", mgv11, mgv12, mgv13, mgv14, mgv15, mgv16, mgv17, mgv18, mgv19);
1287 js["Result"] = false;
1288 js["Error"] = "Bad request parameters";
1292 logger->debug("timeredit: {} {} {} {} {} {} {} {} {}", newName, newActive, newChannelID, newDay, newWeekDays, newStartTime, newStopTime, newPriority, newLifetime);
1295 cTimer* timer = jsonserver_findTimer2(oldName, oldActive, oldChannelID, oldDay, oldWeekDays, oldStartTime, oldStopTime, oldPriority, oldLifetime);
1298 js["Result"] = false;
1299 js["Error"] = "Timer not found";
1303 // Old version commented below used to set each thing individually based on whether it had changed. However, since
1304 // the only way to change the timer channel appears to be with the cTimer::Parse function, might as well use that
1305 // for everything it supports
1306 // Except flags. Get current flags, set using Parse, then add/remove active as needed the other way.
1308 time_t nstt = atoi(newStartTime);
1310 localtime_r(&nstt, &nstm);
1311 int nssf = (nstm.tm_hour * 100) + nstm.tm_min;
1313 time_t nztt = atoi(newStopTime);
1315 localtime_r(&nztt, &nztm);
1316 int nzsf = (nztm.tm_hour * 100) + nztm.tm_min;
1318 strreplace(newName, ':', '|');
1320 cString parseBuffer = cString::sprintf("%u:%s:%s:%04d:%04d:%d:%d:%s:%s",
1321 timer->Flags(), newChannelID, *(cTimer::PrintDay(atoi(newDay), atoi(newWeekDays), true)),
1322 nssf, nzsf, atoi(newPriority), atoi(newLifetime), newName, timer->Aux() ? timer->Aux() : "");
1324 logger->debug("timeredit: new parse: {}", *parseBuffer);
1326 bool parseResult = timer->Parse(*parseBuffer);
1329 js["Result"] = false;
1330 js["Error"] = "Timer parsing failed";
1334 if (timer->HasFlags(tfActive) != !(strcasecmp(newActive, "true")))
1336 logger->debug("timeredit: {} {} set new active: {}", timer->HasFlags(tfActive), !(strcasecmp(newActive, "true")), newActive);
1338 if (strcasecmp(newActive, "true") == 0)
1340 timer->SetFlags(tfActive);
1342 else if (strcasecmp(newActive, "false") == 0)
1344 timer->ClrFlags(tfActive);
1348 js["Result"] = false;
1349 js["Error"] = "Bad request parameters";
1354 js["Result"] = true;
1355 Timers.SetModified();
1360 if (strcmp(timer->File(), newName))
1362 logger->debug("timeredit: set new name: {}", newName);
1363 timer->SetFile(newName);
1366 if (timer->Day() != atoi(newDay))
1368 logger->debug("timeredit: set new day: {}", newDay);
1369 timer->SetDay(atoi(newDay));
1372 if (timer->WeekDays() != atoi(newWeekDays))
1374 logger->debug("timeredit: set new week days: {}", newWeekDays);
1375 timer->SetWeekDays(atoi(newWeekDays));
1378 if (timer->StartTime() != atoi(newStartTime))
1380 logger->debug("timeredit: set new start time: {}", newStartTime);
1381 time_t nstt = atoi(newStartTime);
1383 localtime_r(&nstt, &nstm);
1384 int nssf = (nstm.tm_hour * 100) + nstm.tm_min;
1385 timer->SetStart(nssf);
1388 if (timer->StopTime() != atoi(newStopTime))
1390 logger->debug("timeredit: set new stop time: {}", newStopTime);
1391 time_t nztt = atoi(newStopTime);
1393 localtime_r(&nztt, &nztm);
1394 int nzsf = (nztm.tm_hour * 100) + nztm.tm_min;
1395 timer->SetStop(nzsf);
1398 if (timer->Priority() != atoi(newPriority))
1400 logger->debug("timeredit: set new priority: {}", newPriority);
1401 timer->SetPriority(atoi(newPriority));
1404 if (timer->Lifetime() != atoi(newLifetime))
1406 logger->debug("timeredit: set new lifetime: {}", newLifetime);
1407 timer->SetLifetime(atoi(newLifetime));
1412 cTimer* jsonserver_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)
1414 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1416 int numTimers = Timers.Count();
1418 logger->debug("findtimer2: {} {} {} {} {} {} {} {} {}", rName, rActive, rChannelID, rDay, rWeekDays, rStartTime, rStopTime, rPriority, rLifetime);
1419 for (int i = 0; i < numTimers; i++)
1421 timer = Timers.Get(i);
1423 logger->debug("findtimer2: search: {} {} {} {} {} {} {} {} {}", timer->File(), timer->HasFlags(tfActive), (const char*)timer->Channel()->GetChannelID().ToString(),
1424 (int)timer->Day(), timer->WeekDays(), (int)timer->StartTime(), (int)timer->StopTime(),
1425 timer->Priority(), timer->Lifetime());
1427 if ( (strcmp(timer->File(), rName) == 0)
1428 && (timer->HasFlags(tfActive) == !(strcasecmp(rActive, "true")))
1429 && (strcmp(timer->Channel()->GetChannelID().ToString(), rChannelID) == 0)
1430 && (timer->Day() == atoi(rDay))
1431 && (timer->WeekDays() == atoi(rWeekDays))
1432 && (timer->StartTime() == atoi(rStartTime))
1433 && (timer->StopTime() == atoi(rStopTime))
1434 && (timer->Priority() == atoi(rPriority))
1435 && (timer->Lifetime() == atoi(rLifetime))
1438 logger->debug("findtimer2: found");
1442 logger->debug("findtimer2: no timer found");
1446 cTimer* jsonserver_findTimer(const char* rChannelID, const char* rName, const char* rStartTime, const char* rStopTime, const char* rWeekDays)
1448 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1450 int numTimers = Timers.Count();
1452 for (int i = 0; i < numTimers; i++)
1454 timer = Timers.Get(i);
1456 logger->debug("findtimer: current: {}", (const char*)timer->ToText(true));
1457 logger->debug("findtimer: {}", (const char*)timer->Channel()->GetChannelID().ToString());
1458 logger->debug("findtimer: {}", rChannelID);
1459 logger->debug("findtimer: {}", timer->File());
1460 logger->debug("findtimer: {}", rName);
1461 logger->debug("findtimer: {}", timer->StartTime());
1462 logger->debug("findtimer: {}", rStartTime);
1463 logger->debug("findtimer: {}", timer->StopTime());
1464 logger->debug("findtimer: {}", rStopTime);
1465 logger->debug("findtimer: {}", timer->WeekDays());
1466 logger->debug("findtimer: {}", rWeekDays);
1469 (strcmp(timer->Channel()->GetChannelID().ToString(), rChannelID) == 0)
1470 && (strcmp(timer->File(), rName) == 0)
1471 && (timer->StartTime() == atoi(rStartTime))
1472 && (timer->StopTime() == atoi(rStopTime))
1473 && (timer->WeekDays() == atoi(rWeekDays))
1476 logger->debug("findtimer: found");
1480 logger->debug("findtimer: no timer found");
1484 bool jsonserver_timerdel(Json::Value& js, const char* postData)
1486 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1487 logger->debug("timerdel");
1489 char rChannelID[50]; int mgv1 = mg_get_var(postData, strlen(postData), "ChannelID", rChannelID, 50);
1490 char rName[1024]; int mgv2 = mg_get_var(postData, strlen(postData), "Name", rName, 1024);
1491 char rStartTime[20]; int mgv3 = mg_get_var(postData, strlen(postData), "StartTime", rStartTime, 20);
1492 char rStopTime[20]; int mgv4 = mg_get_var(postData, strlen(postData), "StopTime", rStopTime, 20);
1493 char rWeekDays[50]; int mgv5 = mg_get_var(postData, strlen(postData), "WeekDays", rWeekDays, 20);
1495 if ( (mgv1 == -1) || (mgv2 == -1) || (mgv3 == -1) || (mgv4 == -1) || (mgv5 == -1))
1497 logger->error("timerdel: request mgvs: {} {} {} {} {}", mgv1, mgv2, mgv3, mgv4, mgv5);
1498 js["Result"] = false;
1499 js["Error"] = "Bad request parameters";
1503 logger->debug("timerdel: {}:{}:{}:{}:{}", rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1505 if (Timers.BeingEdited())
1507 logger->debug("timerdel: Unable to delete timer - timers being edited at VDR");
1508 js["Result"] = false;
1509 js["Error"] = "Timers being edited at VDR";
1513 cTimer* timer = jsonserver_findTimer(rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1516 if (timer->Recording())
1518 logger->debug("timerdel: Unable to delete timer - timer is running");
1519 js["Result"] = false;
1520 js["Error"] = "Timer is running";
1527 Timers.SetModified();
1528 js["Result"] = true;
1532 js["Result"] = false;
1533 js["Error"] = "Timer not found";
1537 bool jsonserver_timerisrecording(Json::Value& js, const char* postData)
1539 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1540 logger->debug("timerisrecording");
1542 char rChannelID[50]; int mgv1 = mg_get_var(postData, strlen(postData), "ChannelID", rChannelID, 50);
1543 char rName[1024]; int mgv2 = mg_get_var(postData, strlen(postData), "Name", rName, 1024);
1544 char rStartTime[20]; int mgv3 = mg_get_var(postData, strlen(postData), "StartTime", rStartTime, 20);
1545 char rStopTime[20]; int mgv4 = mg_get_var(postData, strlen(postData), "StopTime", rStopTime, 20);
1546 char rWeekDays[50]; int mgv5 = mg_get_var(postData, strlen(postData), "WeekDays", rWeekDays, 20);
1548 if ( (mgv1 == -1) || (mgv2 == -1) || (mgv3 == -1) || (mgv4 == -1) || (mgv5 == -1))
1550 logger->error("timerisrecording: request mgvs: {} {} {} {} {}", mgv1, mgv2, mgv3, mgv4, mgv5);
1551 js["Result"] = false;
1552 js["Error"] = "Bad request parameters";
1556 logger->debug("timerisrecording: {}:{}:{}:{}:{}", rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1558 cTimer* timer = jsonserver_findTimer(rChannelID, rName, rStartTime, rStopTime, rWeekDays);
1561 js["Recording"] = timer->Recording();
1562 js["Pending"] = timer->Pending();
1563 js["Result"] = true;
1567 js["Result"] = false;
1568 js["Error"] = "Timer not found";
1572 bool jsonserver_tunersstatus(Json::Value& js, const char* postData)
1574 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1575 logger->debug("tunerstatus");
1577 js["NumDevices"] = cDevice::NumDevices();
1579 Json::Value jsdevices(Json::arrayValue);
1581 for (int i = 0; i < cDevice::NumDevices(); i++)
1583 Json::Value oneDevice;
1584 cDevice *d = cDevice::GetDevice(i);
1585 oneDevice["Number"] = d->DeviceNumber();
1586 oneDevice["Type"] = (const char*)d->DeviceType();
1587 oneDevice["Name"] = (const char*)d->DeviceName();
1588 oneDevice["IsPrimary"] = d->IsPrimaryDevice();
1590 const cChannel* cchannel = d->GetCurrentlyTunedTransponder();
1593 oneDevice["Frequency"] = cchannel->Frequency();
1594 oneDevice["SignalStrength"] = d->SignalStrength();
1595 oneDevice["SignalQuality"] = d->SignalQuality();
1600 oneDevice["Frequency"] = 0;
1601 oneDevice["SignalStrength"] = 0;
1602 oneDevice["SignalQuality"] = 0;
1605 jsdevices.append(oneDevice);
1608 js["Devices"] = jsdevices;
1611 Json::Value jstimers(Json::arrayValue);
1612 int numTimers = Timers.Count();
1614 for (int i = 0; i < numTimers; i++)
1616 timer = Timers.Get(i);
1618 if (timer->Recording())
1620 Json::Value oneTimer;
1621 oneTimer["Recording"] = timer->Recording();
1622 oneTimer["StartTime"] = (int)timer->StartTime();
1623 oneTimer["StopTime"] = (int)timer->StopTime();
1624 oneTimer["File"] = timer->File();
1626 cRecordControl* crc = cRecordControls::GetRecordControl(timer);
1629 cDevice* crcd = crc->Device();
1630 oneTimer["DeviceNumber"] = crcd->DeviceNumber();
1634 oneTimer["DeviceNumber"] = Json::Value::null;
1637 const cChannel* channel = timer->Channel();
1640 oneTimer["ChannelName"] = channel->Name();
1644 oneTimer["ChannelName"] = Json::Value::null;
1647 jstimers.append(oneTimer);
1651 js["CurrentRecordings"] = jstimers;
1654 js["Result"] = true;
1659 bool jsonserver_epgsearchsame(Json::Value& js, const char* postData)
1661 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1662 logger->debug("epgsearchsame");
1664 char sAtTime[15]; int mgv1 = mg_get_var(postData, strlen(postData), "time", sAtTime, 15);
1665 char sTitle[1024]; int mgv2 = mg_get_var(postData, strlen(postData), "title", sTitle, 1024);
1667 if ( (mgv1 == -1) || (mgv2 == -1) )
1669 logger->error("epgsearchsame: request mgvs: {} {}", mgv1, mgv2);
1670 js["Result"] = false;
1671 js["Error"] = "Bad request parameters";
1675 int atTime = atoi(sAtTime);
1676 logger->debug("epgsearchsame: request time: {}, title: {}", atTime, sTitle);
1680 cSchedulesLock SchedulesLock;
1681 const cSchedules *schedules = cSchedules::Schedules(SchedulesLock);
1684 js["Result"] = false;
1688 Json::Value jsevents(Json::arrayValue);
1690 const cEvent *event;
1691 for (cSchedule *schedule = schedules->First(); (schedule != NULL); schedule = schedules->Next(schedule))
1693 event = schedule->GetEventAround(atTime);
1694 if (!event) continue; // nothing found on this schedule(channel)
1696 if (!strcmp(event->Title(), sTitle))
1698 Json::Value oneEvent;
1699 oneEvent["ChannelID"] = (const char*)event->ChannelID().ToString();
1700 oneEvent["ID"] = event->EventID();
1701 oneEvent["Time"] = (Json::UInt)event->StartTime();
1702 oneEvent["Duration"] = event->Duration();
1703 oneEvent["Title"] = event->Title() ? event->Title() : "";
1704 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
1705 oneEvent["HasTimer"] = event->HasTimer();
1706 //oneEvent["Description"] = event->Description() ? event->Description() : "";
1707 jsevents.append(oneEvent);
1711 js["Events"] = jsevents;
1712 js["Result"] = true;
1716 bool jsonserver_epgdownload(Json::Value& js, const char* postData)
1718 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1719 logger->debug("epgdownload: '{}'", postData);
1721 cSchedulesLock MutexLock;
1722 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1726 logger->error("epgdownload: Could not get Schedules object");
1727 js["Result"] = false;
1728 js["Error"] = "Internal schedules error (1)";
1732 Json::Value jsevents(Json::arrayValue);
1734 for (cChannel* channel = Channels.First(); channel; channel = Channels.Next(channel))
1736 if (channel->GroupSep()) continue;
1738 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1739 if (!Schedule) continue;
1742 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1744 Json::Value oneEvent;
1745 oneEvent["ChannelNumber"] = channel->Number();
1746 oneEvent["ChannelID"] = (const char*)event->ChannelID().ToString();
1747 oneEvent["ID"] = event->EventID();
1748 oneEvent["Time"] = (Json::UInt)event->StartTime();
1749 oneEvent["Duration"] = event->Duration();
1750 oneEvent["Title"] = event->Title() ? event->Title() : "";
1751 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
1752 oneEvent["HasTimer"] = event->HasTimer();
1753 oneEvent["Description"] = event->Description() ? event->Description() : "";
1754 jsevents.append(oneEvent);
1758 js["Result"] = true;
1759 js["Events"] = jsevents;
1763 bool jsonserver_epgsearch(Json::Value& js, const char* postData)
1765 std::shared_ptr<spd::logger> logger = spd::get("jsonserver_spdlog");
1766 logger->debug("epgsearch: '{}'", postData);
1768 char searchfor[1024]; int mgv1 = mg_get_var(postData, strlen(postData), "searchfor", searchfor, 1024);
1772 logger->error("epgsearch: request mgvs: {}", mgv1);
1773 js["Result"] = false;
1774 js["Error"] = "Bad request parameters";
1778 logger->debug("epgsearch: search for: {}", searchfor);
1780 cSchedulesLock MutexLock;
1781 const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
1785 logger->error("epgsearch: Could not get Schedules object");
1786 js["Result"] = false;
1787 js["Error"] = "Internal schedules error (1)";
1791 Json::Value jsevents(Json::arrayValue);
1793 for (cChannel* channel = Channels.First(); channel; channel = Channels.Next(channel))
1795 if (channel->GroupSep()) continue;
1797 const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
1798 if (!Schedule) continue;
1801 bool founddescription;
1802 for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
1805 founddescription = false;
1807 if (event->Title() && strcasestr(event->Title(), searchfor)) foundtitle = true;
1809 if (!foundtitle && event->Description())
1810 if (strcasestr(event->Description(), searchfor)) founddescription = true;
1812 if (foundtitle || founddescription)
1814 Json::Value oneEvent;
1815 oneEvent["ChannelNumber"] = channel->Number();
1816 oneEvent["ChannelID"] = (const char*)event->ChannelID().ToString();
1817 oneEvent["ID"] = event->EventID();
1818 oneEvent["Time"] = (Json::UInt)event->StartTime();
1819 oneEvent["Duration"] = event->Duration();
1820 oneEvent["Title"] = event->Title() ? event->Title() : "";
1821 oneEvent["ShortText"] = event->ShortText() ? event->ShortText() : "";
1822 oneEvent["HasTimer"] = event->HasTimer();
1823 oneEvent["Description"] = event->Description() ? event->Description() : "";
1824 if (founddescription)
1825 oneEvent["FoundInDesc"] = true;
1827 oneEvent["FoundInDesc"] = false;
1828 jsevents.append(oneEvent);
1833 js["Result"] = true;
1834 js["Events"] = jsevents;