#include <vdr/recording.h>
#include <vdr/menu.h>
#include <vdr/timers.h>
+#include <vdr/channels.h>
+
+/* Locking information from VDR:
++ If a plugin needs to access several of the global lists in parallel, locking must
+ always be done in the sequence Timers, Channels, Recordings, Schedules.
+*/
VDRClient::VDRClient()
{
else if (request == "epgfilteradd") success = epgfilteradd(postFields, returnJSON);
else if (request == "epgfilterdel") success = epgfilterdel(postFields, returnJSON);
}
- catch (BadParamException e)
+ catch (const BadParamException& e)
{
logger->error("Bad parameter in call, paramName: {}", e.param);
returnJSON["Result"] = false;
Json::Value jschannels(Json::arrayValue);
- for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+ LOCK_CHANNELS_READ;
+
+ for (const cChannel *channel = Channels->First(); channel; channel = Channels->Next(channel))
{
if (!channel->GroupSep())
{
logger->debug("reclist");
Json::Value jsrecordings(Json::arrayValue);
- cRecordings Recordings;
- Recordings.Load();
- for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
+
+ LOCK_RECORDINGS_READ;
+
+ for (const cRecording *recording = Recordings->First(); recording; recording = Recordings->Next(recording))
{
Json::Value oneRec;
oneRec["StartTime"] = (Json::UInt)recording->Start();
Json::Value jstimers(Json::arrayValue);
- cTimer *timer;
- int numTimers = Timers.Count();
+ const cTimer *timer;
+
+ LOCK_TIMERS_READ;
+ LOCK_CHANNELS_READ;
+ LOCK_SCHEDULES_READ;
+
+ int numTimers = Timers->Count();
for (int i = 0; i < numTimers; i++)
{
- timer = Timers.Get(i);
+ timer = Timers->Get(i);
Json::Value oneTimer;
oneTimer["Active"] = timer->HasFlags(tfActive);
oneTimer["Recording"] = timer->Recording();
{
int channelNumber = timer->Channel()->Number();
int aroundTime = timer->StartTime() + 1;
- const cEvent* eventAround = getEvent(js, channelNumber, 0, aroundTime);
+ const cEvent* eventAround = getEvent(Channels, Schedules, js, channelNumber, 0, aroundTime);
if (eventAround)
{
oneTimer["EventID"] = eventAround->EventID();
int startTime = getVarInt(postFields, "starttime");
int duration = getVarInt(postFields, "duration");
- cChannel* channel = NULL;
- for (channel = Channels.First(); channel; channel = Channels.Next(channel))
+ Json::Value jsevents(Json::arrayValue);
+
+ LOCK_CHANNELS_READ;
+
+ const cChannel* channel = NULL;
+ for (channel = Channels->First(); channel; channel = Channels->Next(channel))
{
if (channel->GroupSep()) continue;
if (channel->Number() == channelNumber) break;
return true;
}
- cSchedulesLock MutexLock;
- const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
- if (!Schedules)
- {
- logger->error("channelschedule: Could not find requested channel: {}", channelNumber);
- js["Result"] = false;
- js["Error"] = "Internal schedules error (1)";
- return true;
- }
+ LOCK_SCHEDULES_READ;
+
const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
if (!Schedule)
{
return true;
}
- Json::Value jsevents(Json::arrayValue);
-
for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
{
if ((event->StartTime() + event->Duration()) < time(NULL)) continue; //in the past filter
int channelNumber = getVarInt(postFields, "channelnumber");
int eventID = getVarInt(postFields, "eventid");
- const cEvent* event = getEvent(js, channelNumber, eventID, 0);
+ LOCK_CHANNELS_READ;
+ LOCK_SCHEDULES_READ;
+
+ const cEvent* event = getEvent(Channels, Schedules, js, channelNumber, eventID, 0);
if (!event)
{
js["Result"] = false;
bool VDRClient::epgdownload(PFMap& postFields, Json::Value& js)
{
logger->debug("epgdownload");
-
- cSchedulesLock MutexLock;
- const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
-
- if (!Schedules)
- {
- logger->error("epgdownload: Could not get Schedules object");
- js["Result"] = false;
- js["Error"] = "Internal schedules error (1)";
- return true;
- }
-
Json::Value jsevents(Json::arrayValue);
- for (cChannel* channel = Channels.First(); channel; channel = Channels.Next(channel))
+ LOCK_CHANNELS_READ;
+ LOCK_SCHEDULES_READ;
+
+ for (const cChannel* channel = Channels->First(); channel; channel = Channels->Next(channel))
{
if (channel->GroupSep()) continue;
const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
if (!Schedule) continue;
-
for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
{
Json::Value oneEvent;
std::string searchfor = getVarString(postFields, "searchfor");
logger->debug("epgsearch: search for: {}", searchfor);
- cSchedulesLock MutexLock;
- const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
-
- if (!Schedules)
- {
- logger->error("epgsearch: Could not get Schedules object");
- js["Result"] = false;
- js["Error"] = "Internal schedules error (1)";
- return true;
- }
-
Json::Value jsevents(Json::arrayValue);
- for (cChannel* channel = Channels.First(); channel; channel = Channels.Next(channel))
+ LOCK_CHANNELS_READ;
+ LOCK_SCHEDULES_READ;
+
+ for (const cChannel* channel = Channels->First(); channel; channel = Channels->Next(channel))
{
if (channel->GroupSep()) continue;
logger->debug("epgsearchsame: request time: {}, title: {}", atTime, sTitle);
- cSchedulesLock SchedulesLock;
- const cSchedules *schedules = cSchedules::Schedules(SchedulesLock);
- if (!schedules)
- {
- js["Result"] = false;
- return true;
- }
-
Json::Value jsevents(Json::arrayValue);
-
const cEvent *event;
- for (cSchedule *schedule = schedules->First(); (schedule != NULL); schedule = schedules->Next(schedule))
+
+ LOCK_SCHEDULES_READ;
+
+ for (const cSchedule *schedule = Schedules->First(); (schedule != NULL); schedule = Schedules->Next(schedule))
{
event = schedule->GetEventAround(atTime);
if (!event) continue; // nothing found on this schedule(channel)
logger->debug("epgsearchotherhalf");
int channelNumber = getVarInt(postFields, "channelnumber");
int eventID = getVarInt(postFields, "eventid");
+ const cChannel* channel = NULL;
+
+ LOCK_CHANNELS_READ;
- cChannel* channel = NULL;
- for (channel = Channels.First(); channel; channel = Channels.Next(channel))
+ for (channel = Channels->First(); channel; channel = Channels->Next(channel))
{
if (channel->GroupSep()) continue;
if (channel->Number() == channelNumber) break;
return true;
}
- cSchedulesLock MutexLock;
- const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
- if (!Schedules)
- {
- logger->error("epgsearchotherhalf: Could not find requested channel: {}", channelNumber);
- js["Result"] = false;
- js["Error"] = "Internal schedules error (1)";
- return true;
- }
+ LOCK_SCHEDULES_READ;
+
const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
if (!Schedule)
{
Json::Value jstimers(Json::arrayValue);
- int numTimers = Timers.Count();
- cTimer* timer;
+
+ LOCK_TIMERS_READ;
+ LOCK_CHANNELS_READ; // Because this calls timer->Channel() .. necessary?
+
+ int numTimers = Timers->Count();
+ const cTimer* timer;
for (int i = 0; i < numTimers; i++)
{
- timer = Timers.Get(i);
+ timer = Timers->Get(i);
if (timer->Recording())
{
return true;
}
- cTimer *t = Timers.GetTimer(timer);
+ LOCK_TIMERS_WRITE;
+ Timers->SetExplicitModify();
+
+ cTimer *t = Timers->GetTimer(timer);
if (t)
{
delete timer;
return true;
}
- Timers.Add(timer);
- Timers.SetModified();
+ Timers->Add(timer);
+ Timers->SetModified();
+
js["Result"] = true;
return true;
}
std::string reqfilename = getVarString(postFields, "filename");
logger->debug("recinfo: {}", reqfilename);
- cRecordings Recordings;
- Recordings.Load(); // probably have to do this
- cRecording *recording = Recordings.GetByName(reqfilename.c_str());
+ LOCK_RECORDINGS_READ;
+
+ const cRecording *recording = Recordings->GetByName(reqfilename.c_str());
if (!recording)
{
std::string reqfilename = getVarString(postFields, "filename");
logger->debug("recstop: {}", reqfilename);
- cRecordings Recordings;
- Recordings.Load(); // probably have to do this
- cRecording *recording = Recordings.GetByName(reqfilename.c_str());
+ LOCK_TIMERS_WRITE;
+ LOCK_RECORDINGS_WRITE; // May not need write here, but to be safe..
+
+ cRecording *recording = Recordings->GetByName(reqfilename.c_str());
if (!recording)
{
return true;
}
- if (Timers.BeingEdited())
- {
- logger->debug("recstop: timers being edited elsewhere");
- js["Result"] = false;
- return true;
- }
-
cTimer* timer = rc->Timer();
if (!timer)
{
}
timer->ClrFlags(tfActive);
- Timers.SetModified();
js["Result"] = true;
return true;
std::string reqfilename = getVarString(postFields, "filename");
logger->debug("recdel: {}", reqfilename);
- cRecordings Recordings;
- Recordings.Load(); // probably have to do this
- cRecording *recording = Recordings.GetByName(reqfilename.c_str());
+ LOCK_RECORDINGS_WRITE;
+
+ cRecording *recording = Recordings->GetByName(reqfilename.c_str());
if (!recording)
{
if (recording->Delete())
{
- ::Recordings.DelByName(recording->FileName());
+ Recordings->DelByName(recording->FileName());
js["Result"] = true;
}
else
return true;
}
-void VDRClient::pathsForRecordingName(const std::string& recordingName,
+void VDRClient::pathsForRecordingName(const cRecordings* Recordings, const std::string& recordingName,
std::string& dirNameSingleDate,
std::string& dirNameSingleTitle,
std::string& dirNameSingleFolder,
std::string& dirNameFullPathTitle,
std::string& dirNameFullPathDate) // throws int
{
- cRecordings Recordings;
- Recordings.Load();
- cRecording* recordingObj = Recordings.GetByName(recordingName.c_str());
- if (!recordingObj) throw 2;
+ const char* t;
+ {
+ const cRecording* recordingObj = Recordings->GetByName(recordingName.c_str());
+ if (!recordingObj) throw 2;
+ t = recordingObj->FileName();
- cRecordControl *rc = cRecordControls::GetRecordControl(recordingObj->FileName());
- if (rc) throw 3;
+ cRecordControl *rc = cRecordControls::GetRecordControl(recordingObj->FileName());
+ if (rc) throw 3;
- logger->debug("paths: recording: {}", recordingObj->Name());
- logger->debug("paths: recording: {}", recordingObj->FileName());
+ logger->debug("paths: recording: {}", recordingObj->Name());
+ } // unlock recordings
- const char* t = recordingObj->FileName();
- dirNameFullPathDate = t;
+ logger->debug("paths: recording: {}", t);
+ dirNameFullPathDate = t;
int k, j, m;
// Find the datedirname
std::string dirNameFullPathTitle;
std::string dirNameFullPathDate;
+#warning Switch this to Recording->ChangeName ?
+
try
{
- pathsForRecordingName(fileNameToAffect, dirNameSingleDate,
+ LOCK_RECORDINGS_WRITE;
+
+ pathsForRecordingName(Recordings,
+ fileNameToAffect, dirNameSingleDate,
dirNameSingleTitle, dirNameSingleFolder,
dirNameFullPathTitle, dirNameFullPathDate);
// Success. Test for remove old dir containter
rmdir(dirNameFullPathTitle.c_str()); // can't do anything about a fail result at this point.
- ::Recordings.Update();
+ Recordings->Update();
js["Result"] = true;
js["NewRecordingFileName"] = newDirNameFullPathDate;
}
try
{
- pathsForRecordingName(fileNameToAffect, dirNameSingleDate,
+ LOCK_RECORDINGS_WRITE;
+
+ pathsForRecordingName(Recordings,
+ fileNameToAffect, dirNameSingleDate,
dirNameSingleTitle, dirNameSingleFolder,
dirNameFullPathTitle, dirNameFullPathDate);
rmdir(dirNameFullPathFolder.c_str()); // can't do anything about a fail result at this point.
}
- ::Recordings.Update();
+ Recordings->Update();
js["Result"] = true;
js["NewRecordingFileName"] = newDirNameFullPathDate;
}
logger->debug("timersetactive: {} {}:{}:{}:{}:{}", tNewActive, rChannelID, rName, rStartTime, rStopTime, rWeekDays);
- cTimer* timer = findTimer(rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
+ if ((tNewActive != "true") && (tNewActive != "false"))
+ {
+ js["Result"] = false;
+ js["Error"] = "Bad request parameters";
+ return true;
+ }
+
+
+ LOCK_TIMERS_WRITE;
+ Timers->SetExplicitModify();
+
+ cTimer* timer = findTimer(Timers, rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
if (timer)
{
- if (tNewActive == "true") timer->SetFlags(tfActive);
- else if (tNewActive == "false") timer->ClrFlags(tfActive);
- else
- {
- js["Result"] = false;
- js["Error"] = "Bad request parameters";
- return true;
- }
+ if (tNewActive == "true") timer->SetFlags(tfActive);
+ else timer->ClrFlags(tfActive);
js["Result"] = true;
- Timers.SetModified();
+ Timers->SetModified();
return true;
}
logger->debug("timerdel: {}:{}:{}:{}:{}", rChannelID, rName, rStartTime, rStopTime, rWeekDays);
- if (Timers.BeingEdited())
- {
- logger->debug("timerdel: Unable to delete timer - timers being edited at VDR");
- js["Result"] = false;
- js["Error"] = "Timers being edited at VDR";
- return true;
- }
+ LOCK_TIMERS_WRITE;
+ Timers->SetExplicitModify();
- cTimer* timer = findTimer(rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
+ cTimer* timer = findTimer(Timers, rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
if (timer)
{
if (timer->Recording())
return true;
}
- Timers.Del(timer);
- Timers.SetModified();
+ Timers->Del(timer);
+ Timers->SetModified();
js["Result"] = true;
return true;
}
logger->debug("timerisrecording: {}:{}:{}:{}:{}", rChannelID, rName, rStartTime, rStopTime, rWeekDays);
- cTimer* timer = findTimer(rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
+ LOCK_TIMERS_READ;
+
+ const cTimer* timer = findTimer(Timers, rChannelID.c_str(), rName.c_str(), rStartTime.c_str(), rStopTime.c_str(), rWeekDays.c_str());
if (timer)
{
js["Recording"] = timer->Recording();
std::string reqfilename = getVarString(postFields, "filename");
logger->debug("recresetresume: {}", reqfilename);
- cRecordings Recordings;
- Recordings.Load(); // probably have to do this
- cRecording *recording = Recordings.GetByName(reqfilename.c_str());
+ LOCK_RECORDINGS_WRITE; // may not need write, but to be safe..
+
+ cRecording *recording = Recordings->GetByName(reqfilename.c_str());
if (!recording)
{
logger->debug("timeredit: {} {} {} {} {} {} {} {} {}", newName, newActive, newChannelID, newDay, newWeekDays, newStartTime, newStopTime, newPriority, newLifetime);
- 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());
+ LOCK_TIMERS_WRITE;
+ Timers->SetExplicitModify();
+
+ cTimer* timer = findTimer2(Timers, 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());
if (!timer)
{
js["Result"] = false;
return true;
}
+ Timers->SetModified();
+
// Old version commented below (now removed) used to set each thing individually based on whether it had changed. However, since
// the only way to change the timer channel appears to be with the cTimer::Parse function, might as well use that
// for everything it supports
}
js["Result"] = true;
- Timers.SetModified();
return true;
}
js["Error"] = "Channel/Programme not found";
return true;
}
- catch (std::exception e)
+ catch (const std::exception& e)
{
js["Result"] = false;
js["Error"] = "Unknown error";
}
js["EPGFilters"] = jsfilters;
}
- catch (std::exception e)
+ catch (const std::exception& e)
{
js["Result"] = false;
js["Error"] = "Unknown error";
//////////////////////////////////////////////////////////////////////////////////////////////////
-const cEvent* VDRClient::getEvent(Json::Value& js, int channelNumber, int eventID, int aroundTime)
+const cEvent* VDRClient::getEvent(const cChannels* Channels, const cSchedules* Schedules,
+ Json::Value& js, int channelNumber, int eventID, int aroundTime)
{
- cChannel* channel = NULL;
- for (channel = Channels.First(); channel; channel = Channels.Next(channel))
+ const cChannel* channel = NULL;
+
+ for (channel = Channels->First(); channel; channel = Channels->Next(channel))
{
if (channel->GroupSep()) continue;
if (channel->Number() == channelNumber) break;
return NULL;
}
- cSchedulesLock MutexLock;
- const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
- if (!Schedules)
- {
- logger->error("getevent: Could not find requested channel: {}", channelNumber);
- js["Error"] = "Internal schedules error (1)";
- return NULL;
- }
-
const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
if (!Schedule)
{
return event;
}
-cTimer* VDRClient::findTimer(const char* rChannelID, const char* rName, const char* rStartTime, const char* rStopTime, const char* rWeekDays)
+cTimer* VDRClient::findTimer(cTimers* Timers,
+ const char* rChannelID, const char* rName, const char* rStartTime, const char* rStopTime, const char* rWeekDays)
{
- int numTimers = Timers.Count();
+ int numTimers = Timers->Count();
cTimer* timer;
for (int i = 0; i < numTimers; i++)
{
- timer = Timers.Get(i);
+ timer = Timers->Get(i);
+
+ logger->debug("findtimer: current: {}", (const char*)timer->ToText(true));
+ logger->debug("findtimer: {}", (const char*)timer->Channel()->GetChannelID().ToString());
+ logger->debug("findtimer: {}", rChannelID);
+ logger->debug("findtimer: {}", timer->File());
+ logger->debug("findtimer: {}", rName);
+ logger->debug("findtimer: {}", timer->StartTime());
+ logger->debug("findtimer: {}", rStartTime);
+ logger->debug("findtimer: {}", timer->StopTime());
+ logger->debug("findtimer: {}", rStopTime);
+ logger->debug("findtimer: {}", timer->WeekDays());
+ logger->debug("findtimer: {}", rWeekDays);
+
+ if (
+ (strcmp(timer->Channel()->GetChannelID().ToString(), rChannelID) == 0)
+ && (strcmp(timer->File(), rName) == 0)
+ && (timer->StartTime() == atoi(rStartTime))
+ && (timer->StopTime() == atoi(rStopTime))
+ && (timer->WeekDays() == atoi(rWeekDays))
+ )
+ {
+ logger->debug("findtimer: found");
+ return timer;
+ }
+ }
+ logger->debug("findtimer: no timer found");
+ return NULL;
+}
+
+// Differs only from above by taking const Timers and returning const cTimer
+const cTimer* VDRClient::findTimer(const cTimers* Timers,
+ const char* rChannelID, const char* rName, const char* rStartTime, const char* rStopTime, const char* rWeekDays)
+{
+ int numTimers = Timers->Count();
+ const cTimer* timer;
+ for (int i = 0; i < numTimers; i++)
+ {
+ timer = Timers->Get(i);
logger->debug("findtimer: current: {}", (const char*)timer->ToText(true));
logger->debug("findtimer: {}", (const char*)timer->Channel()->GetChannelID().ToString());
return NULL;
}
-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)
+cTimer* VDRClient::findTimer2(cTimers* Timers, 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)
{
- int numTimers = Timers.Count();
+ int numTimers = Timers->Count();
cTimer* timer;
logger->debug("findtimer2: {} {} {} {} {} {} {} {} {}", rName, rActive, rChannelID, rDay, rWeekDays, rStartTime, rStopTime, rPriority, rLifetime);
for (int i = 0; i < numTimers; i++)
{
- timer = Timers.Get(i);
+ timer = Timers->Get(i);
logger->debug("findtimer2: search: {} {} {} {} {} {} {} {} {}", timer->File(), timer->HasFlags(tfActive), (const char*)timer->Channel()->GetChannelID().ToString(),
(int)timer->Day(), timer->WeekDays(), (int)timer->StartTime(), (int)timer->StopTime(),
if (i == postFields.end()) throw BadParamException(paramName);
int r;
try { r = std::stoi(i->second); }
- catch (std::exception e) { throw BadParamException(paramName); }
+ catch (const std::exception& e) { throw BadParamException(paramName); }
return r;
}
return false;
}
}
- catch (libconfig::SettingNotFoundException e)
+ catch (const libconfig::SettingNotFoundException& e)
{
libconfig::Setting& setting = epgFilter.getRoot();
setting.add("filters", libconfig::Setting::Type::TypeList);