vdr-plugin-tvguide/recmanager.c
2014-01-16 16:49:38 +01:00

597 lines
21 KiB
C

#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <vdr/menu.h>
#include "services/remotetimers.h"
#include "services/tvscraper.h"
#include "tools.h"
#include "switchtimer.h"
#include "timerconflict.h"
#include "recmanager.h"
static int CompareRecording(const void *p1, const void *p2) {
return (int)((*(cRecording **)p1)->Start() - (*(cRecording **)p2)->Start());
}
cRecManager::cRecManager(void) {
epgSearchPlugin = NULL;
epgSearchAvailable = false;
}
cRecManager::~cRecManager(void) {
}
void cRecManager::SetEPGSearchPlugin(void) {
epgSearchPlugin = cPluginManager::GetPlugin("epgsearch");
if (epgSearchPlugin) {
epgSearchAvailable = true;
}
}
bool cRecManager::RefreshRemoteTimers(void) {
cString errorMsg;
if (!pRemoteTimers->Service("RemoteTimers::RefreshTimers-v1.0", &errorMsg)) {
esyslog("tvguide: %s", *errorMsg);
return false;
}
return true;
}
bool cRecManager::CheckEventForTimer(const cEvent *event) {
bool hasTimer = false;
if (tvguideConfig.useRemoteTimers && pRemoteTimers) {
RemoteTimers_GetMatch_v1_0 rtMatch;
rtMatch.event = event;
pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
if (rtMatch.timerMatch == tmFull)
hasTimer = true;
} else
hasTimer = event->HasTimer();
return hasTimer;
}
cTimer *cRecManager::GetTimerForEvent(const cEvent *event) {
cTimer *timer = NULL;
if (tvguideConfig.useRemoteTimers && pRemoteTimers) {
RemoteTimers_GetMatch_v1_0 rtMatch;
rtMatch.event = event;
pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
timer = rtMatch.timer;
} else
timer = Timers.GetMatch(event);
return timer;
}
cTimer *cRecManager::createTimer(const cEvent *event, std::string path) {
cTimer *timer = NULL;
if (tvguideConfig.useRemoteTimers && pRemoteTimers) {
timer = createRemoteTimer(event, path);
} else {
timer = createLocalTimer(event, path);
}
return timer;
}
cTimer *cRecManager::createLocalTimer(const cEvent *event, std::string path) {
cTimer *timer = new cTimer(event);
cTimer *t = Timers.GetTimer(timer);
if (t) {
t->OnOff();
t->SetEventFromSchedule();
delete timer;
timer = t;
isyslog("timer %s reactivated", *t->ToDescr());
} else {
Timers.Add(timer);
isyslog("timer %s added (active)", *timer->ToDescr());
}
SetTimerPath(timer, event, path);
Timers.SetModified();
return timer;
}
cTimer *cRecManager::createRemoteTimer(const cEvent *event, std::string path) {
cTimer *t = new cTimer(event);
SetTimerPath(t, event, path);
RemoteTimers_Timer_v1_0 rt;
rt.timer = t;
pRemoteTimers->Service("RemoteTimers::GetTimer-v1.0", &rt.timer);
if (rt.timer) {
rt.timer->OnOff();
if (!pRemoteTimers->Service("RemoteTimers::ModTimer-v1.0", &rt))
rt.timer = NULL;
} else {
rt.timer = t;
if (!pRemoteTimers->Service("RemoteTimers::NewTimer-v1.0", &rt))
isyslog("%s", *rt.errorMsg);
}
RefreshRemoteTimers();
return rt.timer;
}
void cRecManager::SetTimerPath(cTimer *timer, const cEvent *event, std::string path) {
cString newFileName;
if (path.size() > 0) {
std::replace(path.begin(), path.end(), '/', '~');
newFileName = cString::sprintf("%s~%s", path.c_str(), timer->File());
} else {
newFileName = event->Title();
}
if ( !isempty(event->ShortText()) && ((event->Duration() / 60 ) < 70) ) //Add Dir only for Series
newFileName = cString::sprintf("%s~%s", *newFileName, event->ShortText());
timer->SetFile(*newFileName);
}
void cRecManager::DeleteTimer(int timerID) {
cTimer *t = Timers.Get(timerID);
if (!t)
return;
DeleteTimer(t);
}
void cRecManager::DeleteTimer(const cEvent *event) {
if (!event)
return;
if (tvguideConfig.useRemoteTimers && pRemoteTimers) {
DeleteRemoteTimer(event);
} else {
DeleteLocalTimer(event);
}
}
void cRecManager::DeleteLocalTimer(const cEvent *event) {
cTimer *t = Timers.GetMatch(event);
if (!t)
return;
DeleteTimer(t);
}
void cRecManager::DeleteTimer(cTimer *timer) {
if (timer->Recording()) {
timer->Skip();
cRecordControls::Process(time(NULL));
}
isyslog("timer %s deleted", *timer->ToDescr());
Timers.Del(timer, true);
Timers.SetModified();
}
void cRecManager::DeleteRemoteTimer(const cEvent *event) {
RemoteTimers_GetMatch_v1_0 rtMatch;
rtMatch.event = event;
pRemoteTimers->Service("RemoteTimers::GetMatch-v1.0", &rtMatch);
if (rtMatch.timer) {
RemoteTimers_Timer_v1_0 rt;
rt.timer = rtMatch.timer;
isyslog("remotetimer %s deleted", *rt.timer->ToDescr());
if (!pRemoteTimers->Service("RemoteTimers::DelTimer-v1.0", &rt))
isyslog("remotetimer error");
RefreshRemoteTimers();
}
}
void cRecManager::SaveTimer(cTimer *timer, cTimer newTimerSettings) {
if (!timer)
return;
bool active = newTimerSettings.HasFlags(tfActive);
int prio = newTimerSettings.Priority();
int lifetime = newTimerSettings.Lifetime();
time_t day = newTimerSettings.Day();
int start = newTimerSettings.Start();
int stop = newTimerSettings.Stop();
timer->SetDay(day);
timer->SetStart(start);
timer->SetStop(stop);
timer->SetPriority(prio);
timer->SetLifetime(lifetime);
if (timer->HasFlags(tfActive) && !active)
timer->ClrFlags(tfActive);
else if (!timer->HasFlags(tfActive) && active)
timer->SetFlags(tfActive);
timer->SetEventFromSchedule();
if (tvguideConfig.useRemoteTimers && pRemoteTimers) {
RemoteTimers_Timer_v1_0 rt;
rt.timer = timer;
if (!pRemoteTimers->Service("RemoteTimers::ModTimer-v1.0", &rt))
rt.timer = NULL;
RefreshRemoteTimers();
} else {
Timers.SetModified();
}
}
bool cRecManager::IsRecorded(const cEvent *event) {
cTimer *timer = Timers.GetMatch(event);
if (!timer)
return false;
return timer->Recording();
}
cTVGuideTimerConflicts *cRecManager::CheckTimerConflict(void) {
cTVGuideTimerConflicts *conflictList = new cTVGuideTimerConflicts();
if (!epgSearchAvailable)
return conflictList;
Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
std::list<std::string> conflicts = epgSearch->handler->TimerConflictList();
int numConflicts = conflicts.size();
if (numConflicts == 0)
return conflictList;
for (std::list<std::string>::iterator it=conflicts.begin(); it != conflicts.end(); ++it) {
conflictList->AddConflict(*it);
}
}
delete epgSearch;
conflictList->CalculateConflicts();
return conflictList;
}
void cRecManager::CreateSeriesTimer(cTimer *seriesTimer) {
seriesTimer->SetEventFromSchedule();
if (tvguideConfig.useRemoteTimers && pRemoteTimers) {
RemoteTimers_Timer_v1_0 rt;
rt.timer = seriesTimer;
if (!pRemoteTimers->Service("RemoteTimers::NewTimer-v1.0", &rt))
isyslog("%s", *rt.errorMsg);
RefreshRemoteTimers();
} else {
Timers.Add(seriesTimer);
Timers.SetModified();
}
}
void cRecManager::ReadEPGSearchTemplates(std::vector<TVGuideEPGSearchTemplate> *epgTemplates) {
cString ConfigDir = cPlugin::ConfigDirectory("epgsearch");
cString epgsearchConf = "epgsearchtemplates.conf";
cString fileName = AddDirectory(*ConfigDir, *epgsearchConf);
if (access(fileName, F_OK) == 0) {
FILE *f = fopen(fileName, "r");
if (f) {
char *s;
cReadLine ReadLine;
while ((s = ReadLine.Read(f)) != NULL) {
char *p = strchr(s, '#');
if (p)
*p = 0;
stripspace(s);
try {
if (!isempty(s)) {
std::string templ = s;
int posID = templ.find_first_of(":");
int posName = templ.find_first_of(":", posID+1);
std::string name = templ.substr(posID+1, posName - posID - 1);
std::string templValue = templ.substr(posName);
TVGuideEPGSearchTemplate tmp;
tmp.name = name;
tmp.templValue = templValue;
epgTemplates->push_back(tmp);
}
} catch (...){}
}
}
}
}
const cEvent **cRecManager::PerformSearchTimerSearch(std::string epgSearchString, int &numResults) {
if (!epgSearchAvailable)
return NULL;
const cEvent **searchResults = NULL;
Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
std::list<std::string> results = epgSearch->handler->QuerySearch(epgSearchString);
numResults = results.size();
if (numResults > 0) {
searchResults = new const cEvent *[numResults];
cSchedulesLock schedulesLock;
const cSchedules *schedules;
schedules = cSchedules::Schedules(schedulesLock);
const cEvent *event = NULL;
int index=0;
for (std::list<std::string>::iterator it=results.begin(); it != results.end(); ++it) {
try {
splitstring s(it->c_str());
std::vector<std::string> flds = s.split(':', 1);
int eventID = atoi(flds[1].c_str());
std::string channelID = flds[7];
tChannelID chanID = tChannelID::FromString(channelID.c_str());
cChannel *channel = Channels.GetByChannelID(chanID);
if (channel) {
const cSchedule *Schedule = NULL;
Schedule = schedules->GetSchedule(channel);
event = Schedule->GetEvent(eventID);
if (event) {
searchResults[index] = event;
} else
return NULL;
} else
return NULL;
index++;
} catch (...){}
}
}
}
return searchResults;
}
const cEvent **cRecManager::PerformSearch(Epgsearch_searchresults_v1_0 data, int &numResults) {
if (epgSearchAvailable) {
if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) {
cList<Epgsearch_searchresults_v1_0::cServiceSearchResult> *list = data.pResultList;
if (!list)
return NULL;
int numElements = list->Count();
const cEvent **searchResults = NULL;
if (numElements > 0) {
searchResults = new const cEvent *[numElements];
numResults = numElements;
int index = 0;
for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r ; r = list->Next(r)) {
searchResults[index] = r->event;
index++;
}
}
delete list;
return searchResults;
}
}
return NULL;
}
void cRecManager::GetSearchTimers(std::vector<cTVGuideSearchTimer> *searchTimer) {
if (!epgSearchAvailable) {
return;
}
Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
std::list<std::string> searchTimerList;
searchTimerList = epgSearch->handler->SearchTimerList();
for(std::list<std::string>::iterator it = searchTimerList.begin(); it != searchTimerList.end(); it++) {
cTVGuideSearchTimer timer;
timer.SetEPGSearchString(it->c_str());
if (timer.Parse())
searchTimer->push_back(timer);
}
}
}
int cRecManager::CreateSearchTimer(std::string epgSearchString) {
int timerID = -1;
if (!epgSearchAvailable)
return timerID;
Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
timerID = epgSearch->handler->AddSearchTimer(epgSearchString);
}
return timerID;
}
bool cRecManager::SaveSearchTimer(cTVGuideSearchTimer *searchTimer) {
if (!epgSearchAvailable)
return false;
Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
if (searchTimer->GetID() > -1) {
if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
bool success = epgSearch->handler->ModSearchTimer(searchTimer->BuildSearchString());
if (success) {
esyslog("tvguide: search timer with id %d sucessfully modified", searchTimer->GetID());
return true;
} else {
esyslog("tvguide: error modifying search timer with id %d, build string %s", searchTimer->GetID(), searchTimer->BuildSearchString().c_str());
return false;
}
}
} else {
if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
int timerID = epgSearch->handler->AddSearchTimer(searchTimer->BuildSearchString());
if (timerID >=0) {
esyslog("tvguide: search timer with id %d sucessfully created", timerID);
return true;
} else {
esyslog("tvguide: error creating search timer, build string %s", searchTimer->BuildSearchString().c_str());
return false;
}
}
}
return false;
}
void cRecManager::DeleteSearchTimer(cTVGuideSearchTimer *searchTimer, bool delTimers) {
if (!epgSearchAvailable)
return;
int searchTimerID = searchTimer->GetID();
if (delTimers) {
cTimer *timer = Timers.First();
while(timer) {
if (!timer->Recording()) {
char* searchID = GetAuxValue(timer, "s-id");
if (searchID) {
if (searchTimerID == atoi(searchID)) {
cTimer* timerNext = Timers.Next(timer);
DeleteTimer(timer);
timer = timerNext;
} else {
timer = Timers.Next(timer);
}
free(searchID);
} else {
timer = Timers.Next(timer);
}
} else {
timer = Timers.Next(timer);
}
}
}
Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
bool success = epgSearch->handler->DelSearchTimer(searchTimerID);
if (success) {
esyslog("tvguide: search timer \"%s\" sucessfully deleted", searchTimer->SearchString().c_str());
} else {
esyslog("tvguide: error deleting search timer \"%s\"", searchTimer->SearchString().c_str());
}
}
}
void cRecManager::UpdateSearchTimers(void) {
if (epgSearchAvailable) {
Epgsearch_updatesearchtimers_v1_0 data;
data.showMessage = false;
epgSearchPlugin->Service("Epgsearch-updatesearchtimers-v1.0", &data);
}
}
// announceOnly: 0 = switch, 1 = announce only, 2 = ask for switch
bool cRecManager::CreateSwitchTimer(const cEvent *event, cSwitchTimer switchTimer) {
if (epgSearchAvailable && event) {
Epgsearch_switchtimer_v1_0 data;
data.event = event;
data.mode = 1;
data.switchMinsBefore = switchTimer.switchMinsBefore;
data.announceOnly = switchTimer.announceOnly;
data.success = false;
epgSearchPlugin->Service("Epgsearch-switchtimer-v1.0", &data);
cSwitchTimer *t = new cSwitchTimer(event);
SwitchTimers.Add(t);
return data.success;
}
return false;
}
void cRecManager::DeleteSwitchTimer(const cEvent *event) {
SwitchTimers.DeleteSwitchTimer(event);
if (epgSearchAvailable) {
Epgsearch_switchtimer_v1_0 data;
data.event = event;
data.mode = 2;
data.switchMinsBefore = 0;
data.announceOnly = 0;
data.success = false;
epgSearchPlugin->Service("Epgsearch-switchtimer-v1.0", &data);
}
}
cRecording **cRecManager::SearchForRecordings(std::string searchString, int &numResults) {
cRecording **matchingRecordings = NULL;
int num = 0;
numResults = 0;
for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
std::string s1 = recording->Name();
std::string s2 = searchString;
if (s1.empty() || s2.empty()) continue;
// tolerance for fuzzy searching: 90% of the shorter text length, but at least 1
int tolerance = std::max(1, (int)std::min(s1.size(), s2.size()) / 10);
bool match = FindIgnoreCase(s1, s2) >= 0 || FindIgnoreCase(s2, s1) >= 0;
if (!match) {
AFUZZY af = { NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 };
if (s1.size() > 32) s1 = s1.substr(0, 32);
afuzzy_init(s1.c_str(), tolerance, 0, &af);
/* Checking substring */
int res = afuzzy_checkSUB(s2.c_str(), &af);
afuzzy_free(&af);
match = (res > 0);
}
if (!match) {
AFUZZY af = { NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 };
if (s2.size() > 32) s2 = s2.substr(0, 32);
afuzzy_init(s2.c_str(), tolerance, 0, &af);
/* Checking substring */
int res = afuzzy_checkSUB(s1.c_str(), &af);
afuzzy_free(&af);
match = (res > 0);
}
if (match) {
matchingRecordings = (cRecording **)realloc(matchingRecordings, (num + 1) * sizeof(cRecording *));
matchingRecordings[num++] = recording;
}
}
if (num > 0) {
qsort(matchingRecordings, num, sizeof(cRecording *), CompareRecording);
numResults = num;
return matchingRecordings;
}
return NULL;
}
const cEvent **cRecManager::LoadReruns(const cEvent *event, int &numResults) {
if (epgSearchAvailable && !isempty(event->Title())) {
Epgsearch_searchresults_v1_0 data;
std::string strQuery = event->Title();
if (tvguideConfig.useSubtitleRerun > 0) {
if (tvguideConfig.useSubtitleRerun == 2 || !isempty(event->ShortText()))
strQuery += "~";
if (!isempty(event->ShortText()))
strQuery += event->ShortText();
data.useSubTitle = true;
} else {
data.useSubTitle = false;
}
data.query = (char *)strQuery.c_str();
data.mode = 0;
data.channelNr = 0;
data.useTitle = true;
data.useDescription = false;
if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) {
cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>* list = data.pResultList;
if (!list)
return NULL;
const cEvent **searchResults = NULL;
int numElements = list->Count();
if (numElements > 0) {
searchResults = new const cEvent *[numElements];
int index = 0;
for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r; r = list->Next(r)) {
if ((event->ChannelID() == r->event->ChannelID()) && (event->StartTime() == r->event->StartTime()))
continue;
searchResults[index] = r->event;
index++;
}
delete list;
numResults = index;
return searchResults;
}
}
}
return NULL;
}
void cRecManager::GetFavorites(std::vector<cTVGuideSearchTimer> *favorites) {
if (!epgSearchAvailable) {
return;
}
Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1;
if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) {
std::list<std::string> searchTimerList;
searchTimerList = epgSearch->handler->SearchTimerList();
for(std::list<std::string>::iterator it = searchTimerList.begin(); it != searchTimerList.end(); it++) {
cTVGuideSearchTimer timer;
timer.SetEPGSearchString(it->c_str());
if (timer.Parse()) {
if (timer.UseInFavorites())
favorites->push_back(timer);
}
}
}
}