/* * searchtimer.c * * See the README file for copyright information * */ #include "python.h" #include "searchtimer.h" #include //*************************************************************************** // //*************************************************************************** int cSearchTimer::searchField[] = { sfTitle, sfFolge, sfDescription, 0 }; const char* cSearchTimer::searchFieldName[] = { "TITLE", "SHORTTEXT", "COMPLONGDESCRIPTION", 0 }; int cSearchTimer::repeadCheckField[] = { sfTitle, // 1 sfFolge, // 2 sfDescription, // 4 0 }; const char* cSearchTimer::repeadCheckFieldName[] = { "COMPTITLE", // <- "EPISODECOMPNAME" <- "EPISODECOMPSHORTNAME" "COMPSHORTTEXT", // <- "EPISODECOMPPARTNAME" "COMPLONGDESCRIPTION", 0 }; //*************************************************************************** // Class Search Timer //*************************************************************************** cSearchTimer::cSearchTimer(cFrame* aParent) : startValue("START", cDBS::ffInt, 10), endValue("END", cDBS::ffInt, 10) { parent = aParent; connection = 0; useeventsDb = 0; searchtimerDb = 0; timerDb = 0; timersDoneDb = 0; mapDb = 0; vdrDb = 0; selectChannelFromMap = 0; selectDoneTimer = 0; selectActiveSearchtimers = 0; selectSearchtimerMaxModSp = 0; selectAllTimer = 0; selectRPTimerByEvent = 0; selectTimerByEventId = 0; selectConflictingTimers = 0; selectFailedTimerByEvent = 0; selectSearchTimerByName = 0; selectSearchTimerById = 0; selectEvent = 0; lastSearchTimerUpdate = 0; ptyRecName = new Python("recording", "name"); } cSearchTimer::~cSearchTimer() { delete ptyRecName; } int cSearchTimer::init(const char* confDir) { if (ptyRecName->init(confDir) != success) { tell(0, "Error: Init of python script recording.py failed, aborting"); return fail; } return done; } int cSearchTimer::initDb() { int status = success; exitDb(); connection = new cDbConnection(); useeventsDb = new cDbTable(connection, "useevents"); if ((status = useeventsDb->open(yes) != success)) return status; searchtimerDb = new cDbTable(connection, "searchtimers"); if (searchtimerDb->open(yes) != success) return fail; timerDb = new cDbTable(connection, "timers"); if (timerDb->open(yes) != success) return fail; timersDoneDb = new cDbTable(connection, "timersdone"); if (timersDoneDb->open(yes) != success) return fail; mapDb = new cDbTable(connection, "channelmap"); if ((status = mapDb->open(yes)) != success) return status; vdrDb = new cDbTable(connection, "vdrs"); if ((status = vdrDb->open(yes)) != success) return status; // ---------- // select ... // from searchtimers // where state <> 'D' and active > 0 // and (type = 'R' or type is null) selectActiveSearchtimers = new cDbStatement(searchtimerDb); selectActiveSearchtimers->build("select "); selectActiveSearchtimers->bindAllOut(); selectActiveSearchtimers->bind("UPDSP", cDBS::bndOut, ", "); selectActiveSearchtimers->bind("INSSP", cDBS::bndOut, ", "); selectActiveSearchtimers->build(" from %s where %s <> 'D' and %s > 0 and (%s = 'R' or %s is null)", searchtimerDb->TableName(), searchtimerDb->getField("STATE")->getDbName(), searchtimerDb->getField("ACTIVE")->getDbName(), searchtimerDb->getField("TYPE")->getDbName(), searchtimerDb->getField("TYPE")->getDbName()); status += selectActiveSearchtimers->prepare(); // -------------------- // select max(modsp) from searchtimers selectSearchtimerMaxModSp = new cDbStatement(searchtimerDb); selectSearchtimerMaxModSp->build("select "); selectSearchtimerMaxModSp->bind("MODSP", cDBS::bndOut, "max("); selectSearchtimerMaxModSp->build(") from %s", searchtimerDb->TableName()); status += selectSearchtimerMaxModSp->prepare(); // ---------- // select * from searchtimers // where state != 'D' // and name = ? selectSearchTimerByName = new cDbStatement(searchtimerDb); selectSearchTimerByName->build("select "); selectSearchTimerByName->bindAllOut(); selectSearchTimerByName->build(" from %s where %s != 'D'", searchtimerDb->TableName(), searchtimerDb->getField("STATE")->getDbName()); selectSearchTimerByName->bind("NAME", cDBS::bndIn | cDBS::bndSet, " and "); status += selectSearchTimerByName->prepare(); // ---------- // select * from searchtimers // where state != 'D' // and id = ? selectSearchTimerById = new cDbStatement(searchtimerDb); selectSearchTimerById->build("select "); selectSearchTimerById->bindAllOut(); selectSearchTimerById->build(" from %s where %s != 'D'", searchtimerDb->TableName(), searchtimerDb->getField("STATE")->getDbName()); selectSearchTimerById->bind("ID", cDBS::bndIn | cDBS::bndSet, " and "); status += selectSearchTimerById->prepare(); // ---------- // select id // from timersdone // ... selectDoneTimer = new cDbStatement(timersDoneDb); // selectDoneTimer - will build and prepared later at runtime ... // ---------- // select channelname // from channelmap selectChannelFromMap = new cDbStatement(mapDb); selectChannelFromMap->build("select "); selectChannelFromMap->bind("CHANNELNAME", cDBS::bndOut); selectChannelFromMap->build(" from %s where ", mapDb->TableName()); selectChannelFromMap->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet); status += selectChannelFromMap->prepare(); // select t.*, v.name, v.tunercount // from timers t, vdrs v // where t.state in ('P','R') // and t.vdruuid = v.uuid // order by t.day, t.starttime selectAllTimer = new cDbStatement(timerDb); selectAllTimer->build("select "); selectAllTimer->setBindPrefix("t."); selectAllTimer->bindAllOut(); selectAllTimer->setBindPrefix("v."); selectAllTimer->bind(vdrDb, "NAME", cDBS::bndOut, ", "); selectAllTimer->bind(vdrDb, "TUNERCOUNT", cDBS::bndOut, ", "); selectAllTimer->build(" from %s t, %s v ", timerDb->TableName(), vdrDb->TableName()); selectAllTimer->build(" where t.%s in ('P','R')", timerDb->getField("STATE")->getDbName()); selectAllTimer->build(" and t.VDRUUID = v.UUID"); selectAllTimer->build(" order by t.%s, t.%s", timerDb->getField("DAY")->getDbName(), timerDb->getField("STARTTIME")->getDbName()); status += selectAllTimer->prepare(); // select * // from timers where // eventid = ? selectRPTimerByEvent = new cDbStatement(timerDb); selectRPTimerByEvent->build("select "); selectRPTimerByEvent->bindAllOut(); selectRPTimerByEvent->build(" from %s where ", timerDb->TableName()); selectRPTimerByEvent->bind("EVENTID", cDBS::bndIn | cDBS::bndSet); status += selectRPTimerByEvent->prepare(); // select * // from timers where // state not in ('D','E') // eventid = ? and channelid = ? selectTimerByEventId = new cDbStatement(timerDb); selectTimerByEventId->build("select "); selectTimerByEventId->bindAllOut(); selectTimerByEventId->build(" from %s where ", timerDb->TableName()); selectTimerByEventId->build(" %s not in ('D', 'E')", timerDb->getField("STATE")->getDbName()); selectTimerByEventId->bind("EVENTID", cDBS::bndIn | cDBS::bndSet, " and "); selectTimerByEventId->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet, " and "); status += selectTimerByEventId->prepare(); /* // select * from timers // where state in ('P','R') // and active = 1 // and day + starttime div 100 * 60 * 60 + starttime % 100 * 60 >= ? // and day + starttime div 100 * 60 * 60 + starttime % 100 * 60 <= ? // and vdruuid = ? // group by SUBSTRING_INDEX(channelid, '-', 3); selectConflictingTimers = new cDbStatement(timerDb); selectConflictingTimers->build("select "); selectConflictingTimers->bindAllOut(); selectConflictingTimers->build(" from %s where %s in ('P','R')", timerDb->TableName(), timerDb->getField("STATE")->getDbName()); selectConflictingTimers->build(" and active = 1"); selectConflictingTimers->bindText("day + starttime div 100 * 60 * 60 + starttime % 100 * 60", &startValue, ">=", " and "); selectConflictingTimers->bindText("day + starttime div 100 * 60 * 60 + starttime % 100 * 60", &endValue, "<=", " and "); selectConflictingTimers->bind("VDRUUID", cDBS::bndIn | cDBS::bndSet, " and "); selectConflictingTimers->build(" group by SUBSTRING_INDEX(channelid, '-', 3)"); status += selectConflictingTimers->prepare(); */ // select * from timers // where state in ('P','R') // and active = 1 // and day + starttime div 100 * 60 * 60 + starttime % 100 * 60 >= ? // and day + starttime div 100 * 60 * 60 + starttime % 100 * 60 <= ? // and vdruuid = ? selectConflictingTimers = new cDbStatement(timerDb); selectConflictingTimers->build("select "); selectConflictingTimers->bindAllOut(); selectConflictingTimers->build(" from %s where %s in ('P','R')", timerDb->TableName(), timerDb->getField("STATE")->getDbName()); selectConflictingTimers->build(" and active = 1"); selectConflictingTimers->bindText("day + starttime div 100 * 60 * 60 + starttime % 100 * 60", &startValue, ">=", " and "); selectConflictingTimers->bindText("day + starttime div 100 * 60 * 60 + starttime % 100 * 60", &endValue, "<=", " and "); selectConflictingTimers->bind("VDRUUID", cDBS::bndIn | cDBS::bndSet, " and "); status += selectConflictingTimers->prepare(); // select * from timers // where // eventid = ? // and channelid = ? // and vdruuid = ? // and state = ? selectFailedTimerByEvent = new cDbStatement(timerDb); selectFailedTimerByEvent->build("select "); selectFailedTimerByEvent->bindAllOut(); selectFailedTimerByEvent->build(" from %s where ", timerDb->TableName()); selectFailedTimerByEvent->bind("EVENTID", cDBS::bndIn | cDBS::bndSet); selectFailedTimerByEvent->bind("CHANNELID", cDBS::bndIn | cDBS::bndSet, " and "); selectFailedTimerByEvent->bind("VDRUUID", cDBS::bndIn | cDBS::bndSet, " and "); selectFailedTimerByEvent->bind("STATE", cDBS::bndIn | cDBS::bndSet, " and "); selectFailedTimerByEvent->prepare(); // select * // from eventsviewplain where // useid = ? selectEvent = new cDbStatement(useeventsDb); selectEvent->build("select "); selectEvent->bindAllOut(); selectEvent->build(" from eventsviewplain where "); selectEvent->bind("USEID", cDBS::bndIn | cDBS::bndSet); status += selectEvent->prepare(); // ---------- if (status != success) { tell(0, "Error: At least %d statements not prepared successfully", status*-1); return status; } return success; } int cSearchTimer::exitDb() { if (connection) { delete selectActiveSearchtimers; selectActiveSearchtimers = 0; delete selectSearchtimerMaxModSp; selectSearchtimerMaxModSp = 0; delete selectDoneTimer; selectDoneTimer = 0; delete selectChannelFromMap; selectChannelFromMap = 0; delete selectAllTimer; selectAllTimer = 0; delete selectConflictingTimers; selectConflictingTimers = 0; delete selectRPTimerByEvent; selectRPTimerByEvent = 0; delete selectTimerByEventId; selectTimerByEventId = 0; delete selectFailedTimerByEvent; selectFailedTimerByEvent = 0; delete selectSearchTimerByName; selectSearchTimerByName = 0; delete selectSearchTimerById; selectSearchTimerById = 0; delete selectEvent; selectEvent = 0; delete mapDb; mapDb = 0; delete useeventsDb; useeventsDb = 0; delete searchtimerDb; searchtimerDb = 0; delete timerDb; timerDb = 0; delete timersDoneDb; timersDoneDb = 0; delete vdrDb; vdrDb = 0; delete connection; connection = 0; } return success; } //*************************************************************************** // Check Connection //*************************************************************************** int cSearchTimer::checkConnection() { static int retry = 0; // check connection if (!dbConnected(yes)) { // try to connect tell(0, "Trying to re-connect to database!"); retry++; if (initDb() != success) { tell(0, "Retry #%d failed!", retry); exitDb(); return fail; } retry = 0; tell(0, "Connection established successfull!"); } return success; } //*************************************************************************** // Any Search Timer Modified //*************************************************************************** int cSearchTimer::modified() { int modsp = 0; if (selectSearchtimerMaxModSp->find()) modsp = searchtimerDb->getIntValue("MODSP"); selectSearchtimerMaxModSp->freeResult(); return modsp && modsp > lastSearchTimerUpdate; } //*************************************************************************** // Prepare Search Statement //*************************************************************************** cDbStatement* cSearchTimer::prepareSearchStatement(cDbRow* searchTimer) { cDbTable* db = useeventsDb; cDbStatement* select = new cDbStatement(db); const char* searchOp = "="; const char* expression = searchTimer->getStrValue("EXPRESSION"); const char* expression1 = searchTimer->getStrValue("EXPRESSION1"); const char* episodename = searchTimer->getStrValue("EPISODENAME"); const char* season = searchTimer->getStrValue("SEASON"); const char* seasonpart = searchTimer->getStrValue("SEASONPART"); const char* category = searchTimer->getStrValue("CATEGORY"); const char* genre = searchTimer->getStrValue("GENRE"); const char* tipp = searchTimer->getStrValue("TIPP"); const char* year = searchTimer->getStrValue("YEAR"); const char* chformat = searchTimer->getStrValue("CHFORMAT"); int noepgmatch = searchTimer->getIntValue("NOEPGMATCH"); int searchmode = searchTimer->getIntValue("SEARCHMODE"); int searchfields = searchTimer->getIntValue("SEARCHFIELDS"); int searchfields1 = searchTimer->getIntValue("SEARCHFIELDS1"); int casesensitiv = searchTimer->getIntValue("CASESENSITIV"); int weekdays = searchTimer->getValue("WEEKDAYS")->isNull() ? (int)na : searchTimer->getIntValue("WEEKDAYS"); switch (searchmode) { case smExact: searchOp = casesensitiv ? "= BINARY" : "="; break; case smRegexp: searchOp = casesensitiv ? "regexp BINARY" : "regexp"; break; case smLike: searchOp = casesensitiv ? "like BINARY" : "like"; break; case smContained: searchOp = casesensitiv ? "like BINARY" : "like"; break; } select->build("select "); select->bindAllOut(); select->setBindPrefix("c."); select->bind(mapDb, "FORMAT", cDBS::bndOut, ", "); select->clrBindPrefix(); select->build(" from eventsviewplain e, (select distinct channelid,channelname,format,ord,visible from %s) c where ", mapDb->TableName()); select->build("e.%s = c.%s", db->getField("CHANNELID")->getDbName(), mapDb->getField("CHANNELID")->getDbName()); select->build(" and e.updflg in (%s)", cEventState::getVisible()); select->build(" and e.cnt_starttime >= unix_timestamp()-120"); // not more than 2 minutes running // search fields 1 if (!isEmpty(expression) && strcmp(expression, "%") != 0 && strcmp(expression, "%%") != 0 && searchfields) { select->build(" and ("); for (int i = 0, n = 0; searchField[i]; i++) { if (!db->getField(searchFieldName[i])) tell(0, "Fatal: Search field '%s' not known!", searchFieldName[i]); else if (searchfields & searchField[i]) { select->build("%s(%s %s '%s%s%s')", n++ ? " or " : "", db->getField(searchFieldName[i])->getDbName(), searchOp, searchmode == smContained ? "%" : "", connection->escapeSqlString(expression).c_str(), searchmode == smContained ? "%" : ""); } } select->build(")"); } // search fields 2 if (!isEmpty(expression1) && strcmp(expression1, "%") != 0 && strcmp(expression1, "%%") != 0 && searchfields1) { select->build(" and ("); for (int i = 0, n = 0; searchField[i]; i++) { if (!db->getField(searchFieldName[i])) tell(0, "Fatal: Search field '%s' not known!", searchFieldName[i]); else if (searchfields1 & searchField[i]) { select->build("%s(%s %s '%s%s%s')", n++ ? " or " : "", db->getField(searchFieldName[i])->getDbName(), searchOp, searchmode == smContained ? "%" : "", connection->escapeSqlString(expression1).c_str(), searchmode == smContained ? "%" : ""); } } select->build(")"); } // Channel Format (CHFORMAT) if (!isEmpty(chformat)) { std::string format; format = "'" + strReplace(",", "','", chformat) + "'"; select->build(" and ("); select->build(" c.%s in (%s) ", mapDb->getField("FORMAT")->getDbName(), format.c_str()); select->build(")"); } // Kategorie 'Spielfilm','Serie' (CATEGORY) if (!isEmpty(category)) { select->build(" and ("); if (noepgmatch) select->build("%s is null or ", db->getField("CATEGORY")->getDbName()); select->build(" %s in (%s) ", db->getField("CATEGORY")->getDbName(), category); select->build(")"); } // Genre 'Krimi','Action' (GENRE) if (!isEmpty(genre)) { select->build(" and ("); if (noepgmatch) select->build("%s is null or ", db->getField("GENRE")->getDbName()); select->build(" %s in (%s) ", db->getField("GENRE")->getDbName(), genre); select->build(")"); } // Tipp (TIPP) if (!isEmpty(tipp)) { select->build(" and ("); if (noepgmatch) select->build("%s is null or ", db->getField("TIPP")->getDbName()); select->build(" %s in (%s) ", db->getField("TIPP")->getDbName(), tipp); select->build(")"); } // Serien Titel (EPISODENAME) if (!isEmpty(episodename)) { select->build(" and ("); select->build(" %s = BINARY '%s' or (%s is null and %s = BINARY '%s')", db->getField("EPISODENAME")->getDbName(), connection->escapeSqlString(episodename).c_str(), db->getField("EPISODENAME")->getDbName(), db->getField("TITLE")->getDbName(), connection->escapeSqlString(episodename).c_str()); select->build(")"); } // Staffel like 3-5 (SEASON) if (!isEmpty(season)) { select->build(" and ("); if (noepgmatch) select->build("%s is null or ", db->getField("EPISODESEASON")->getDbName()); select->build(" %s between %d and %d ", db->getField("EPISODESEASON")->getDbName(), rangeFrom(season), rangeTo(season)); select->build(")"); } // Staffelfolge (SEASONPART) if (!isEmpty(seasonpart)) { select->build(" and ("); if (noepgmatch) select->build("%s is null or ", db->getField("EPISODEPART")->getDbName()); select->build(" %s between %d and %d ", db->getField("EPISODEPART")->getDbName(), rangeFrom(seasonpart), rangeTo(seasonpart)); select->build(")"); } // Jahr (YEAR) if (!isEmpty(year)) { select->build(" and ("); if (noepgmatch) select->build("%s is null or ", db->getField("YEAR")->getDbName()); select->build(" %s between %d and %d ", db->getField("YEAR")->getDbName(), rangeFrom(year), rangeTo(year)); select->build(")"); } if (weekdays > 0) { select->build(" and "); select->build("(%d & (1 << weekday(from_unixtime(%s)))) <> 0", weekdays, db->getField("STARTTIME")->getDbName()); } select->build(" order by e.cnt_starttime, c.ord"); if (select->prepare() != success) { delete select; select = 0; tell(0, "Error: AUTOTIMER: Prepare of statement for searchtimer failed, skipping"); return 0; } const char* p = strstr(select->asText(), " from "); tell(2, "AUTOTIMER: Search statement [%s;]", p ? p : select->asText()); return select; } //*************************************************************************** // Check Timers // - check existing, pending timers against epg // - check existing, pending timers against searchtimers //*************************************************************************** int cSearchTimer::checkTimers() { int count = 0; tell(1, "Checking timers against actual epg and serarchtimer settings"); if (checkConnection() != success) return 0; timerDb->clear(); vdrDb->clear(); for (int f = selectAllTimer->find(); f; f = selectAllTimer->fetch()) { long aid = na; int namingmode = timerDb->getIntValue("NAMINGMODE"); const char* tmplExpression = timerDb->getStrValue("TEMPLATE"); // check only active and pending timers with eventid if (!timerDb->getIntValue("ACTIVE") || !timerDb->hasValue("STATE", "P") || !timerDb->getIntValue("EVENTID")) continue; if (timerDb->hasValue("SOURCE", "epgs")) // 'epgs' => epgsearch plugin continue; // ------------------- // lookups if (timerDb->getIntValue("AUTOTIMERID")) { searchtimerDb->clear(); searchtimerDb->setValue("ID", timerDb->getIntValue("AUTOTIMERID")); if (selectSearchTimerById->find()) { aid = searchtimerDb->getIntValue("ID"); namingmode = searchtimerDb->getIntValue("NAMINGMODE"); tmplExpression = timerDb->getStrValue("TEMPLATE"); } } tell(2, "Checking timer(%ld) of event (%ld) against EPG", timerDb->getIntValue("ID"), timerDb->getIntValue("EVENTID")); useeventsDb->clear(); useeventsDb->setValue("USEID", timerDb->getIntValue("EVENTID")); if (!selectEvent->find()) { if (aid != na) { parent->message(0, 'W', "EPGD: Timer action", "Event (%ld) '%s' of timer (%ld) '%s' / autotimer (%ld) " "not found; timer rejected!", timerDb->getIntValue("EVENTID"), useeventsDb->getStrValue("TITLE"), timerDb->getIntValue("ID"), timerDb->getStrValue("FILE"), aid); modifyTimer(timerDb, taReject); count++; } else { int warningCount = timerDb->getIntValue("WRNCOUNT"); if (!warningCount) { warningCount++; parent->message(0, 'W', "EPGD: Timer action", "Event (%ld) '%s' of timer (%ld) '%s' not found." "It's a manual timer, take your own solution!", timerDb->getIntValue("EVENTID"), useeventsDb->getStrValue("TITLE"), timerDb->getIntValue("ID"), timerDb->getStrValue("FILE")); timerDb->setValue("WRNCOUNT", warningCount); timerDb->update(); } } continue; } // ------------------------ // work - check timer data // Check Start Time if (!timerDb->hasValue("_STARTTIME", useeventsDb->getIntValue("STARTTIME"))) { // if difference < 2 minutes adjust silent if (abs(timerDb->getIntValue("_STARTTIME") - useeventsDb->getIntValue("STARTTIME")) > 2*tmeSecondsPerMinute) parent->message(0, 'I', "EPGD: Timer action", "Info: Starttime of event (%ld) '%s' changed from '%s' to '%s', updating timer (%ld)", timerDb->getIntValue("EVENTID"), useeventsDb->getStrValue("TITLE"), l2pTime(timerDb->getIntValue("_STARTTIME")).c_str(), l2pTime(useeventsDb->getIntValue("STARTTIME")).c_str(), timerDb->getIntValue("ID")); modifyTimer(timerDb, taAdjust); count++; } // Check recording name if (namingmode != tnmDefault) { // execute python - calc recording name if (ptyRecName->execute(useeventsDb, namingmode, tmplExpression) == success) { if (!timerDb->hasValue("FILE", ptyRecName->getResult())) { parent->message(0, 'I', "EPGD: Timer action", "Calculated name of event (%ld) changed from '%s' to '%s', updating timer (%ld)!", timerDb->getIntValue("EVENTID"), timerDb->getStrValue("FILE"), ptyRecName->getResult(), timerDb->getIntValue("ID")); timerDb->setValue("FILE", ptyRecName->getResult()); modifyTimer(timerDb, taModify); count++; } } } selectEvent->freeResult(); selectSearchTimerById->freeResult(); } selectAllTimer->freeResult(); tell(1, "Timers check done"); return count; } //*************************************************************************** // Modify Timer //*************************************************************************** int cSearchTimer::modifyTimer(cDbTable* timerDb, TimerAction action) { // create a 'M'odify request ... timerDb->setCharValue("ACTION", action); timerDb->getValue("STATE")->setNull(); timerDb->update(); tell(0, "Created '%s' request for timer (%ld) at vdr '%s'", toName(action), timerDb->getIntValue("ID"), timerDb->getStrValue("VDRUUID")); // triggerVdrs("TIMERJOB", timerDb->getStrValue("VDRUUID")); return success; } //*************************************************************************** // Match Criterias //*************************************************************************** int cSearchTimer::matchCriterias(cDbRow* searchTimer, cDbRow* event) { const char* channelids = searchTimer->getStrValue("CHANNELIDS"); int chexclude = searchTimer->getIntValue("CHEXCLUDE"); int rangeStart = searchTimer->getValue("STARTTIME")->isNull() ? (int)na : searchTimer->getIntValue("STARTTIME"); int rangeEnd = searchTimer->getValue("ENDTIME")->isNull() ? (int)na : searchTimer->getIntValue("ENDTIME"); int nextDays = searchTimer->getValue("NEXTDAYS")->isNull() ? (int)na : searchTimer->getIntValue("NEXTDAYS"); const char* channelid = event->getStrValue("CHANNELID"); time_t starttime = event->getIntValue("STARTTIME"); int hhmm = l2hhmm(starttime); // check if channel known to VDRs mapDb->clear(); mapDb->setValue("CHANNELID", channelid); if (!selectChannelFromMap->find() || mapDb->getIntValue("UNKNOWNATVDR") > 0) { mapDb->reset(); tell(3, "AUTOTIMER: Skipping hit, channelid '%s' is unknown at least on one VDR!", event->getStrValue("CHANNELID")); return no; } mapDb->reset(); // check channel matches if (!isEmpty(channelids)) { if (!chexclude && !strstr(channelids, channelid)) { tell(3, "AUTOTIMER: Skipping hit due to channelid - '%s' not in '%s'", event->getStrValue("CHANNELID"), channelids); return no; } else if (chexclude && strstr(channelids, channelid)) { tell(3, "AUTOTIMER: Skipping hit due to channelid - '%s' it in '%s'", event->getStrValue("CHANNELID"), channelids); return no; } } // check start if (rangeStart > 0 && hhmm < rangeStart) { tell(2, "AUTOTIMER: Skipping due to range (start before range)"); return no; } if (rangeEnd > 0 && hhmm > rangeEnd) { tell(3, "AUTOTIMER: Skipping due to range (start behind range)"); return no; } if (nextDays > 0 && event->getIntValue("STARTTIME") > time(0) + tmeSecondsPerDay * nextDays) { tell(3, "AUTOTIMER: Skipping event starting at '%s' due to nextdays of (%d) (start behind range)", l2pTime(event->getIntValue("STARTTIME")).c_str(), nextDays); return no; } // check range ... is start event in nextdays ... return yes; } //*************************************************************************** // Get Search Matches //*************************************************************************** int cSearchTimer::getSearchMatches(cDbRow* searchTimer, json_t* obj, int start, int max) { cDbStatement* select = 0; long searchtimerid = searchTimer->getIntValue("ID"); const char* searchtimername = searchTimer->getStrValue("NAME"); int count = 0; int end = max != na ? start + max : INT_MAX; if (checkConnection() != success) return fail; searchtimerDb->clear(); if (searchtimerid > 0) { searchtimerDb->setValue("ID", searchtimerid); if (!searchtimerDb->find()) { tell(0, "Warning: Searchtimer %ld not found!", searchtimerid); return fail; } searchTimer = searchtimerDb->getRow(); } else if (!isEmpty(searchtimername)) { searchtimerDb->setValue("NAME", searchtimername); if (!selectSearchTimerByName->find()) { tell(0, "Warning: Searchtimer '%s' not found!", searchtimername); return fail; } searchTimer = searchtimerDb->getRow(); } if (!(select = prepareSearchStatement(searchTimer))) return fail; json_t* oEvents = json_array(); useeventsDb->clear(); for (int res = select->find(); res; res = select->fetch()) { useeventsDb->find(); // get all fields .. if (!matchCriterias(searchTimer, useeventsDb->getRow())) continue; count++; if (count < start || count >= end) continue; // add to json object json_t* oData = json_object(); addFieldToJson(oData, useeventsDb, "USEID", no, "id"); addFieldToJson(oData, useeventsDb, "CHANNELID"); addFieldToJson(oData, useeventsDb, "STARTTIME"); addFieldToJson(oData, useeventsDb, "DURATION"); addFieldToJson(oData, useeventsDb, "CATEGORY"); addFieldToJson(oData, useeventsDb, "GENRE"); addFieldToJson(oData, useeventsDb, "TITLE"); addFieldToJson(oData, useeventsDb, "SHORTTEXT"); addFieldToJson(oData, useeventsDb, "SHORTDESCRIPTION"); addFieldToJson(oData, useeventsDb, "NUMRATING"); addFieldToJson(oData, useeventsDb, "TIPP"); addFieldToJson(oData, useeventsDb, "IMAGECOUNT"); isAlreadyDone(searchTimer->getIntValue("REPEATFIELDS"), oData, yes); // timer for this Event pending? timerDb->clear(); timerDb->setValue("EVENTID", useeventsDb->getIntValue("USEID")); timerDb->setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID")); if (selectTimerByEventId->find()) addFieldToJson(oData, timerDb, "ID", no, "TIMERID"); selectTimerByEventId->freeResult(); json_array_append_new(oEvents, oData); } select->freeResult(); delete select; searchtimerDb->reset(); json_object_set_new(obj, "start", json_integer(start)); json_object_set_new(obj, "count", json_integer(count)); json_object_set_new(obj, "events", oEvents); return success; } //*************************************************************************** // Get Done For Event by Searchtimer //*************************************************************************** int cSearchTimer::getDoneFor(cDbRow* searchTimer, cDbRow* useevent, json_t* obj) { if (checkConnection() != success) return fail; searchtimerDb->clear(); searchtimerDb->setValue("ID", searchTimer->getIntValue("ID")); if (!searchtimerDb->find()) { tell(0, "Warning: Searchtimer %ld not found", searchTimer->getIntValue("ID")); return fail; } useeventsDb->clear(); useeventsDb->setBigintValue("CNTEVENTID", useevent->getBigintValue("CNTEVENTID")); useeventsDb->setValue("CHANNELID", useevent->getStrValue("CHANNELID")); useeventsDb->setValue("CNTSOURCE", useevent->getStrValue("CNTSOURCE")); if (!useeventsDb->find()) { tell(0, "Warning: Event '%s/%lu/%s' not found", useevent->getStrValue("CHANNELID"), useevent->getBigintValue("CNTEVENTID"), useevent->getStrValue("CNTSOURCE")); return fail; } isAlreadyDone(searchtimerDb->getIntValue("REPEATFIELDS"), obj, yes); useeventsDb->reset(); searchtimerDb->reset(); return success; } //*************************************************************************** // Update Search Timers //*************************************************************************** int cSearchTimer::updateSearchTimers(int force, const char* reason) { uint64_t start = cMyTimeMs::Now(); long total = 0; tell(1, "AUTOTIMER: Updating searchtimers due to '%s' %s", reason, force ? "(force)" : ""); if (checkConnection() != success) return fail; searchtimerDb->clear(); for (int res = selectActiveSearchtimers->find(); res; res = selectActiveSearchtimers->fetch()) { long hits = 0; cDbStatement* select = 0; // searchtimer updated after last run or force? if (!force && searchtimerDb->getIntValue("MODSP") <= searchtimerDb->getIntValue("LASTRUN")) continue; select = prepareSearchStatement(searchtimerDb->getRow()); if (!select) { lastSearchTimerUpdate = time(0); // protect for infinite call on error return 0; } useeventsDb->clear(); for (int res = select->find(); res; res = select->fetch()) { time_t starttime = useeventsDb->getIntValue("STARTTIME"); int weekday = weekdayOf(starttime); tell(3, "AUTOTIMER: Found event (%s) '%s' / '%s' (%ld/%s) at day %d", l2pTime(starttime).c_str(), useeventsDb->getStrValue("TITLE"), useeventsDb->getStrValue("SHORTTEXT"), useeventsDb->getIntValue("USEID"), useeventsDb->getStrValue("CHANNELID"), weekday); useeventsDb->find(); // get all fields .. // match if (!matchCriterias(searchtimerDb->getRow(), useeventsDb->getRow())) { useeventsDb->reset(); continue; } // check if event already recorded or schedule for recording if (!isAlreadyDone(searchtimerDb->getIntValue("REPEATFIELDS"))) { timerDb->clear(); timerDb->setValue("EVENTID", useeventsDb->getIntValue("USEID")); if (selectRPTimerByEvent->find()) { tell(3, "AUTOTIMER: Timer for event (%ld) '%s/%s' already scheduled, skipping", useeventsDb->getIntValue("USEID"), useeventsDb->getStrValue("TITLE"), useeventsDb->getStrValue("SHORTTEXT")); } else { // #TODO time_t lEndTime = useeventsDb->getIntValue("DURATION"); // #TODO int count = getUsedTransponderAt(useeventsDb->getIntValue("STARTTIME"), lEndTime); // #TODO if (count <= availableTransponders) { if (createTimer(searchtimerDb->getIntValue("ID")) == success) hits++; } // else // tell(1, "AUTOTIMER: Skipping due to"); } selectRPTimerByEvent->freeResult(); useeventsDb->reset(); } } total += hits; select->freeResult(); delete select; searchtimerDb->find(); searchtimerDb->setValue("LASTRUN", time(0)); if (hits) searchtimerDb->setValue("HITS", searchtimerDb->getIntValue("HITS") + hits); searchtimerDb->update(); } selectActiveSearchtimers->freeResult(); lastSearchTimerUpdate = time(0); tell(1, "AUTOTIMER: Update done after %s, created (%ld) timers", ms2Dur(cMyTimeMs::Now()-start).c_str(), total); return total; } //*************************************************************************** // Is Alreayd Done //*************************************************************************** int cSearchTimer::isAlreadyDone(int repeatfields, json_t* obj, int silent) { // std::string chkFields = ""; if (repeatfields <= 0) return no; tell(3, "Check if '%s/%s' already recorded by fields (%d)", useeventsDb->getStrValue("TITLE"), useeventsDb->getStrValue("SHORTTEXT"), repeatfields); // -------------------------------------- // check timersdone to avoid repeading // prepare statement ... selectDoneTimer->clear(); selectDoneTimer->build("select "); selectDoneTimer->bind("ID", cDBS::bndOut); selectDoneTimer->bind("STATE", cDBS::bndOut, ", "); selectDoneTimer->build(" from %s where ", timersDoneDb->TableName()); // retry only 'F'ailed and re'J'ected timers, don't retry 'D'eleted timers sice they are deleted by user /* select id, state, title,comptitle, shorttext,compshorttext from timersdone where state not in ('F','J') and (field('DERSTAATSANWALTDASLUDER', ifnull(comptitle,''),ifnull(episodecompshortname,'')) > 0 or field('',ifnull(comptitle,''),ifnull(episodecompshortname,'NoShortnameAvailable')) > 0) and (field('',ifnull(compshorttext,''),ifnull(episodecomppartname,'')) > 0 or field('',ifnull(compshorttext,''),ifnull(episodecomppartname,'')) > 0); */ selectDoneTimer->build(" %s not in ('F','J')", // mysql ignoring case by default! timersDoneDb->getField("STATE")->getDbName()); if (repeatfields & sfTitle) { selectDoneTimer->build(" and (field('%s', ifnull(comptitle,''),ifnull(episodecompshortname,'')) > 0" " or field('%s',ifnull(comptitle,''),ifnull(episodecompshortname,'NoShortnameAvailable')) > 0)", useeventsDb->getStrValue("COMPTITLE"), useeventsDb->getStrValue("EPISODECOMPSHORTNAME")); } if (repeatfields & sfFolge) { selectDoneTimer->build(" and (field('%s',ifnull(compshorttext,''),ifnull(episodecomppartname,'')) > 0" " or field('%s',ifnull(compshorttext,''),ifnull(episodecomppartname,'')) > 0)", useeventsDb->getStrValue("COMPSHORTTEXT"), useeventsDb->getStrValue("EPISODECOMPPARTNAME")); } if (selectDoneTimer->prepare() != success) { tell(0, "Error: AUTOTIMER: Prepare of statement for 'done' check failed, skipping"); return yes; } timersDoneDb->clear(); json_t* oDones = json_array(); int cnt = 0; for (int f = selectDoneTimer->find(); f; f = selectDoneTimer->fetch()) { json_t* oData = json_object(); long id = timersDoneDb->getIntValue("ID"); const char* state = timersDoneDb->getStrValue("STATE"); if (!silent) tell(3, "AUTOTIMER: The timer/recording exists with timerdone " "id %ld and state '%s', checked by '%s', skipping ...", id, state, selectDoneTimer->asText()); if (!obj) { selectDoneTimer->freeResult(); return yes; } // add to json object json_object_set_new(oData, "id", json_integer(id)); json_object_set_new(oData, "state", json_string(state)); json_array_append_new(oDones, oData); cnt++; selectDoneTimer->freeResult(); } if (obj && cnt) json_object_set_new(obj, "dones", oDones); return no; } //*************************************************************************** // Create Timer // // - searchtimerDb and useeventsDb has to be positioned and loaded //*************************************************************************** int cSearchTimer::createTimer(int id) { int status; int timerid; int retry = no; int doneid = 0; if (checkConnection() != success) return fail; const char* channelname = ""; int namingmode = searchtimerDb->getIntValue("NAMINGMODE"); const char* tmplExpression = searchtimerDb->getStrValue("TEMPLATE"); // ------------------------------------------ // lookup channel name to store in timersdone // (just as a additional 'debug' info) mapDb->clear(); mapDb->setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID")); if (selectChannelFromMap->find()) channelname = mapDb->getStrValue("CHANNELNAME"); if (isEmpty(channelname)) channelname = useeventsDb->getStrValue("CHANNELID"); selectChannelFromMap->freeResult(); // ------------------------------------------ // Create timer in timerdistribution ... cDbRow timerRow("timers"); timerRow.clear(); timerRow.setValue("ID", na); // 'na' is the signal for modifyCreateTimer to create new timer timerRow.setValue("ACTIVE", yes); timerRow.setValue("TYPE", searchtimerDb->getStrValue("TYPE")); timerRow.setValue("SOURCE", "epgd"); timerRow.setValue("EVENTID", useeventsDb->getIntValue("USEID")); timerRow.setValue("_STARTTIME", useeventsDb->getIntValue("STARTTIME")); timerRow.setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID")); timerRow.setValue("NAMINGMODE", namingmode); timerRow.setValue("TEMPLATE", tmplExpression); timerRow.setValue("CHILDLOCK", searchtimerDb->getIntValue("CHILDLOCK")); timerRow.setValue("VDRUUID", searchtimerDb->getStrValue("VDRUUID")); timerRow.setValue("VPS", searchtimerDb->getIntValue("VPS")); timerRow.setValue("PRIORITY", searchtimerDb->getIntValue("PRIORITY")); timerRow.setValue("LIFETIME", searchtimerDb->getIntValue("LIFETIME")); timerRow.setValue("DIRECTORY", searchtimerDb->getStrValue("DIRECTORY")); timerRow.setValue("AUTOTIMERID", searchtimerDb->getIntValue("ID")); timerRow.setValue("AUTOTIMERNAME", searchtimerDb->getStrValue("NAME")); timerRow.setValue("AUTOTIMERINSSP", searchtimerDb->getIntValue("INSSP")); timerRow.setValue("EXPRESSION", searchtimerDb->getStrValue("EXPRESSION")); if (namingmode != tnmDefault) { // execupe python - calc recording name if (ptyRecName->execute(useeventsDb, namingmode, tmplExpression) == success) { tell(0, "Info: The recording name calculated by 'recording.py' is '%s'", ptyRecName->getResult()); if (!isEmpty(ptyRecName->getResult())) timerRow.setValue("FILE", ptyRecName->getResult()); } } // for 'event' based timers, check on failed attemps for this eventid if (!timerRow.getValue("EVENTID")->isEmpty()) { timerDb->clear(); timerDb->setValue("EVENTID", timerRow.getIntValue("EVENTID")); timerDb->setValue("CHANNELID", timerRow.getStrValue("CHANNELID")); timerDb->setValue("VDRUUID", timerRow.getStrValue("VDRUUID")); timerDb->setCharValue("STATE", tsError); // max three retrys .. if (selectFailedTimerByEvent->find()) { if (timerDb->getIntValue("RETRYS") > 3) { tell(0, "Error: AUTOTIMER: Aborting timer create for event %ld, already %ld attempts failed ", timerDb->getIntValue("EVENTID"), timerDb->getIntValue("RETRYS")); selectFailedTimerByEvent->freeResult(); return fail; } retry = yes; doneid = timerDb->getIntValue("DONEID"); timerRow.setValue("ID", timerDb->getIntValue("ID")); // signal for modifyCreateTimer to modify the existing timer timerRow.setValue("RETRYS", timerDb->getIntValue("RETRYS")+1); } selectFailedTimerByEvent->freeResult(); } status = modifyCreateTimer(&timerRow, timerid, /*resetState = */ retry); // on scuccess add to timersdone .. if (status == success) { timersDoneDb->clear(); if (doneid) { timersDoneDb->setValue("ID", doneid); if (!timersDoneDb->find()) doneid = 0; } timersDoneDb->setCharValue("STATE", tdsTimerRequested); timersDoneDb->setValue("SOURCE", "epgd"); timersDoneDb->setValue("CHANNELID", useeventsDb->getStrValue("CHANNELID")); timersDoneDb->setValue("STARTTIME", useeventsDb->getIntValue("STARTTIME")); timersDoneDb->setValue("DURATION", useeventsDb->getIntValue("DURATION")); timersDoneDb->setValue("EXPRESSION", searchtimerDb->getStrValue("EXPRESSION")); timersDoneDb->setValue("AUTOTIMERID", id); timersDoneDb->setValue("AUTOTIMERNAME", searchtimerDb->getStrValue("NAME")); timersDoneDb->setValue("TIMERID", timerid); timersDoneDb->setValue("TITLE", useeventsDb->getStrValue("TITLE")); timersDoneDb->setValue("COMPTITLE", useeventsDb->getStrValue("COMPTITLE")); if (!useeventsDb->getValue("SHORTTEXT")->isEmpty()) timersDoneDb->setValue("SHORTTEXT", useeventsDb->getStrValue("SHORTTEXT")); if (!useeventsDb->getValue("COMPSHORTTEXT")->isEmpty()) timersDoneDb->setValue("COMPSHORTTEXT", useeventsDb->getStrValue("COMPSHORTTEXT")); if (!useeventsDb->getValue("LONGDESCRIPTION")->isEmpty()) timersDoneDb->setValue("LONGDESCRIPTION", useeventsDb->getStrValue("LONGDESCRIPTION")); if (!useeventsDb->getValue("COMPLONGDESCRIPTION")->isEmpty()) timersDoneDb->setValue("COMPLONGDESCRIPTION", useeventsDb->getStrValue("COMPLONGDESCRIPTION")); if (!useeventsDb->getValue("EPISODECOMPNAME")->isEmpty()) timersDoneDb->setValue("EPISODECOMPNAME", useeventsDb->getStrValue("EPISODECOMPNAME")); if (!useeventsDb->getValue("EPISODECOMPSHORTNAME")->isEmpty()) timersDoneDb->setValue("EPISODECOMPSHORTNAME", useeventsDb->getStrValue("EPISODECOMPSHORTNAME")); if (!useeventsDb->getValue("EPISODECOMPPARTNAME")->isEmpty()) timersDoneDb->setValue("EPISODECOMPPARTNAME", useeventsDb->getStrValue("EPISODECOMPPARTNAME")); if (!useeventsDb->getValue("EPISODELANG")->isEmpty()) timersDoneDb->setValue("EPISODELANG", useeventsDb->getStrValue("EPISODELANG")); if (!useeventsDb->getValue("EPISODESEASON")->isEmpty()) timersDoneDb->setValue("EPISODESEASON", useeventsDb->getIntValue("EPISODESEASON")); if (!useeventsDb->getValue("EPISODEPART")->isEmpty()) timersDoneDb->setValue("EPISODEPART", useeventsDb->getIntValue("EPISODEPART")); if (!isEmpty(channelname)) timersDoneDb->setValue("CHANNELNAME", channelname); if (doneid) { timersDoneDb->update(); } else { timersDoneDb->insert(); doneid = timersDoneDb->getLastInsertId(); } parent->message(1, 'I', "EPGD: Timer action", "Created timer (%d) for event '%s - %s / %s on channel '%s' at '%s', doneid is (%d)", timerid, l2pTime(useeventsDb->getIntValue("STARTTIME")).c_str(), useeventsDb->getStrValue("TITLE"), useeventsDb->getStrValue("SHORTTEXT"), channelname, toWeekdayName(weekdayOf(useeventsDb->getIntValue("STARTTIME"))), doneid); timerDb->clear(); timerDb->setValue("ID", timerid); timerDb->setValue("VDRUUID", searchtimerDb->getStrValue("VDRUUID")); if (timerDb->find()) { timerDb->setValue("DONEID", doneid); timerDb->update(); } } return status; } //*************************************************************************** // Modify Timer (copy paste from cMenuDb of epg2vdr/httpd) // // - timerRow contains the destination vdrUuid //*************************************************************************** int cSearchTimer::modifyCreateTimer(cDbRow* timerRow, int& newid, int createRetry) { int status = success; int timerid = timerRow->getIntValue("ID"); int knownTimer = timerid != na; int move = no; newid = na; connection->startTransaction(); timerDb->clear(); // lookup known (existing) timer if (knownTimer) { timerDb->copyValues(timerRow, cDBS::ftPrimary); if (!timerDb->find()) { connection->commit(); tell(0, "Error: Timer (%d) at vdr '%s' not found, aborting modify request!", timerid, timerDb->getStrValue("VDRUUID")); return fail; } // found and all values are loaded! // move to another vdr? if (!timerDb->hasValue("VDRUUID", timerRow->getStrValue("VDRUUID"))) move = yes; } else { timerDb->setValue("VDRUUID", timerRow->getStrValue("VDRUUID")); } if (move) { // request 'D'elete of 'old' timer timerDb->setCharValue("ACTION", taDelete); timerDb->setValue("SOURCE", timerRow->getStrValue("SOURCE")); status = timerDb->update(); // triggerVdrs("TIMERJOB", timerDb->getStrValue("VDRUUID")); // create new on other vdr timerDb->copyValues(timerRow, cDBS::ftData); // takeover all data (can be modified by user) timerDb->setValue("ID", 0); timerDb->setCharValue("ACTION", taCreate); status += timerDb->insert(); newid = timerDb->getLastInsertId(); if (status == success) tell(1, "Created 'move' request for timer (%d) at vdr '%s'", timerid, timerDb->getStrValue("VDRUUID")); } else { // create 'C'reate oder 'M'odify request ... timerDb->copyValues(timerRow, cDBS::ftData); // createRetry ... verry special case ... if (createRetry) { timerDb->getValue("STATE")->setNull(); timerDb->setCharValue("ACTION", taCreate); } else { timerDb->setCharValue("ACTION", knownTimer ? taModify : taCreate); } if (knownTimer) { status = timerDb->update(); newid = timerid; } else { status = timerDb->insert(); newid = timerDb->getLastInsertId(); } if (status == success) { tell(1, "Created '%s' request for timer (%d), event (%ld) at vdr '%s' by autotimer (%ld)", knownTimer ? "modify" : "create", newid, timerDb->getIntValue("EVENTID"), timerDb->getStrValue("VDRUUID"), timerDb->getIntValue("AUTOTIMERID")); } } connection->commit(); // triggerVdrs("TIMERJOB", timerDb->getStrValue("VDRUUID")); return status; } //*************************************************************************** // Check Timer Conficts //*************************************************************************** int cSearchTimer::checkTimerConflicts(std::string& mailBody) { int conflicts = 0; tell(1, "TCC: Starting timer conflict check"); mailBody = ""; if (checkConnection() != success) return fail; timerDb->clear(); vdrDb->clear(); for (int f = selectAllTimer->find(); f; f = selectAllTimer->fetch()) { if (!timerDb->getIntValue("ACTIVE")) continue; if (timerDb->getIntValue("TCCMAILCNT") > 0) continue; tell(2, "TCC: Check conflicts for timer (%ld) '%s' on '%s'; channel '%s'", timerDb->getIntValue("ID"), timerDb->getStrValue("FILE"), vdrDb->getStrValue("NAME"), timerDb->getStrValue("CHANNELID")); // calc 'start' and 'end' time of this timer .. int sDay = timerDb->getIntValue("DAY"); int sTime = timerDb->getIntValue("STARTTIME"); int eDay = timerDb->getIntValue("DAY"); int eTime = timerDb->getIntValue("ENDTIME"); if (eTime < sTime) eDay += tmeSecondsPerDay; time_t lStartTime = sDay + sTime / 100 * tmeSecondsPerHour + sTime % 100 * tmeSecondsPerMinute; time_t lEndTime = eDay + eTime / 100 * tmeSecondsPerHour + eTime % 100 * tmeSecondsPerMinute; // check for conflicts std::string mailPart; int tunerCount = getUsedTransponderAt(lStartTime, lEndTime, mailPart); if (tunerCount > vdrDb->getIntValue("TUNERCOUNT")) { conflicts++; tell(1, "TCC: Timer (%ld) '%s' conflict at '%s - %s' needed %d transponders on '%s'", timerDb->getIntValue("ID"), timerDb->getStrValue("FILE"), l2pTime(lStartTime).c_str(), l2pTime(lEndTime).c_str(), tunerCount, vdrDb->getStrValue("NAME")); mailBody += "" "" " conflict #" + num2Str(conflicts) + " on VDR '" + vdrDb->getStrValue("NAME") + "' at " + l2pDate(lStartTime) + "" ""; mailBody += mailPart; mailBody += ""; timerDb->setValue("TCCMAILCNT", timerDb->getIntValue("TCCMAILCNT") + 1); timerDb->update(); // #TODO - reject autotimer ... ? // rejectTimer(timerDb->getRow()); } else if (tunerCount <= 0) // DEBUG-tell - to be removed? tell(1, "TCC: Fatal got 0 used transponders for timer (%ld) between %s (%ld) and %s (%ld)", timerDb->getIntValue("ID"), l2pTime(lStartTime).c_str(), lStartTime, l2pTime(lEndTime).c_str(), lEndTime); } selectAllTimer->freeResult(); tell(1, "TCC: Finished timer conflict check with (%d) conflicts", conflicts); return conflicts; } //*************************************************************************** // Get Used Transponder At //*************************************************************************** int cSearchTimer::getUsedTransponderAt(time_t lStartTime, time_t lEndTime, std::string& mailPart) { char buf[1024+TB]; if (checkConnection() != success) return fail; startValue.setValue(lStartTime); endValue.setValue(lEndTime); std::map transponders; std::map::iterator it; for (int f = selectConflictingTimers->find(); f; f = selectConflictingTimers->fetch()) { cTccTimerData timer; std::string transponder = timerDb->getStrValue("CHANNELID"); size_t endpos = transponder.find_last_of("-"); const char* channelName = "unknown"; mapDb->clear(); mapDb->setValue("CHANNELID", timerDb->getStrValue("CHANNELID")); if (selectChannelFromMap->find()) channelName = mapDb->getStrValue("CHANNELNAME"); selectChannelFromMap->freeResult(); if (endpos == std::string::npos) continue; transponder = transponder.substr(0, endpos); // build transponder name transponders[transponder].count++; timer.id = timerDb->getIntValue("ID"); timer.file = timerDb->getStrValue("FILE"); timer.begin = timerDb->getIntValue("STARTTIME"); timer.end = timerDb->getIntValue("ENDTIME"); timer.channel = channelName; transponders[transponder].timers.push_back(timer); } selectConflictingTimers->freeResult(); for (it = transponders.begin(); it != transponders.end(); it++) { std::list::iterator li; for (li = it->second.timers.begin(); li != it->second.timers.end(); ++li) { tell(3, "TCC: found (%ld) '%s'", (*li).id, (*li).file.c_str()); sprintf(buf, "" "
%s
" "
%s
" "
%ld
" "
%s
" "
%s
" "
%s
" "", it->first.c_str(), (*li).channel.c_str(), (*li).id, (*li).file.c_str(), hhmm2pTime((*li).begin).c_str(), hhmm2pTime((*li).end).c_str()); mailPart += buf; } } return transponders.size(); } // //*************************************************************************** // // Reject Timer // //*************************************************************************** // int cSearchTimer::rejectTimer(cDbRow* timerRow) // { // tell(1, "Rejecting timer (%ld)", timerRow->getIntValue("ID")); // timerJobsDb->clear(); // timerJobsDb->setValue("TIMERID", timerRow->getIntValue("ID")); // timerJobsDb->setValue("DONEID", timerRow->getIntValue("DONEID")); // timerJobsDb->setValue("STATE", "J"); // timerJobsDb->setValue("ASSUMED", "N"); // timerJobsDb->insert(); // tell(1, "Created delete job for timer (%ld)", timerRow->getIntValue("ID")); // return success; // }