Esempio n. 1
0
eOSState cMenuEpgTimers::Delete(void)
{
  // Check if this timer is active:
  cTimer *ti = CurrentTimer();
  if (ti) {
     if (Interface->Confirm(tr("Delete timer?"))) {
        if (ti->Recording()) {
           if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
              ti->Skip();
              cRecordControls::Process(time(NULL));
              }
           else
              return osContinue;
           }
        isyslog("deleting timer %s", *ti->ToDescr());
        if(!Timers.Del(ti))
            Skins.Message(mtError, trVDR("Could not delete timer"));
        //cOsdMenu::Del(Current());
        Timers.SetModified();
        //Display();
        }
  } else {
      cMenuSwitchTimerItem *item = dynamic_cast<cMenuSwitchTimerItem*> (Get(Current()));
      if (item && item->switchTimer &&
              Interface->Confirm(tr("Delete switchtimer?"))) {
          cMutexLock SwitchTimersLock(&SwitchTimers);
          SwitchTimers.Del(item->switchTimer);
          SwitchTimers.Save();
          Del(Current());
      }
  }
  return osContinue;
}
Esempio n. 2
0
eOSState cMenuEditSwitchTimer::ProcessKey(eKeys Key)
{
    int iOldMinsBefore = data.switchMinsBefore;
    eOSState state = cOsdMenu::ProcessKey(Key);

    if (iOldMinsBefore != data.switchMinsBefore)
    {
	time_t now = time(NULL);
	if (data.event->StartTime() - 60 * data.switchMinsBefore < now)
	    data.switchMinsBefore = iOldMinsBefore;
	Set();
	Display();
    }

    if (state == osUnknown) {
	switch (Key) {
	    case kOk:
	    {
		if (switchTimer)
		{
		    *switchTimer = data;
		    cMutexLock SwitchTimersLock(&SwitchTimers);
		    if (addIfConfirmed)
			SwitchTimers.Add(switchTimer);	      
		    SwitchTimers.Save();
		}
		addIfConfirmed = false;
		return osBack;
		break;
	    } 
	    default: break;
	}
  }
    return state;
}
Esempio n. 3
0
eOSState cMenuSwitchTimers::DeleteAll(void)
{
    if (Interface->Confirm(tr("Edit$Delete all entries?"))) 
    {
	cMutexLock SwitchTimersLock(&SwitchTimers);
	while (SwitchTimers.First()) 
	    SwitchTimers.Del(SwitchTimers.First());
	SwitchTimers.Save();
	Set();
    }

    return osContinue;
}
Esempio n. 4
0
eOSState cMenuSwitchTimers::Delete(void)
{
    cSwitchTimer *curSwitchTimer = CurrentSwitchTimer();
    if (curSwitchTimer) {
	if (Interface->Confirm(tr("Edit$Delete entry?"))) {
	    cMutexLock SwitchTimersLock(&SwitchTimers);
	    SwitchTimers.Del(curSwitchTimer);
	    SwitchTimers.Save();
	    cOsdMenu::Del(Current());
	    Display();
	}
    }
    return osContinue;
}
Esempio n. 5
0
void cMenuSwitchTimers::Set()
{
    Clear();
    cMutexLock SwitchTimersLock(&SwitchTimers);
    cSwitchTimer* switchTimer = SwitchTimers.First();
    while (switchTimer) {
	if (switchTimer->event)
	    Add(new cMenuSwitchTimerItem(switchTimer));
	switchTimer = SwitchTimers.Next(switchTimer);
    }
    Display();
    SetHelp(trVDR("Button$Edit"), tr("Button$Delete all"), trVDR("Button$Delete"), NULL);
    Sort();
}
Esempio n. 6
0
eOSState cMenuSwitchTimers::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);
  if (state == osUnknown) {
    switch (Key) {
	case kOk:
	    state = Summary();
	    break;
	case kGreen:
	    state = DeleteAll();
	    break;
	case kYellow:
	    state = Delete();
	    break;
	case kRed:
	    if (HasSubMenu())
		return osContinue;
	    if (CurrentSwitchTimer())
		state = AddSubMenu(new cMenuEditSwitchTimer(CurrentSwitchTimer()));
	    else
		state = osContinue;
	    break;
	case k0:
	    if (CurrentSwitchTimer())
	    {
		cSwitchTimer* switchTimer = CurrentSwitchTimer();
	        switchTimer->announceOnly = 1 - switchTimer->announceOnly;
		cMutexLock SwitchTimersLock(&SwitchTimers);
		SwitchTimers.Save();
		RefreshCurrent();
		Display();
	    }
	    break;
      default: break;
    }
  }

  return state;
}
Esempio n. 7
0
bool cPluginEpgsearch::Service(const char *Id, void *Data)
{
   if (strcmp(Id, "MainMenuHooksPatch-v1.0::osSchedule") == 0 && EPGSearchConfig.ReplaceOrgSchedule!=0)
   {
      if (Data == NULL)
         return true;
      cOsdMenu **menu = (cOsdMenu**) Data;
      if (menu)
         *menu = (cOsdMenu*) MainMenuAction();
      return true;
   }

   if (strcmp(Id, "Epgsearch-search-v1.0") == 0) {
      if (Data == NULL)
         return true;
      cSearchExt* SearchExt = new cSearchExt;

      Epgsearch_search_v1_0* searchData = (Epgsearch_search_v1_0*) Data;
      searchData->pResultMenu = NULL;
      strn0cpy(SearchExt->search,searchData->query, sizeof(SearchExt->search)); 
      if (searchData->channelNr > 0)
      {
#if VDRVERSNUM > 20300
         LOCK_CHANNELS_READ;
         const cChannels *vdrchannels = Channels;
#else
         cChannels *vdrchannels = &Channels;
#endif
         SearchExt->useChannel = true;
         SearchExt->channelMin = vdrchannels->GetByNumber(searchData->channelNr);
         SearchExt->channelMax = vdrchannels->GetByNumber(searchData->channelNr);
      }
      SearchExt->mode = searchData->mode;
      SearchExt->useTitle = searchData->useTitle;
      SearchExt->useSubtitle = searchData->useSubTitle;
      SearchExt->useDescription = searchData->useDescription;
      searchData->pResultMenu = new cMenuSearchResultsForSearch(SearchExt, cTemplFile::GetTemplateByName("MenuSearchResults"));

      return true;
   }
   if (strcmp(Id, "Epgsearch-exttimeredit-v1.0") == 0 && !EPGSearchConfig.useVDRTimerEditMenu) {
      if (Data == NULL)
         return true;

      Epgsearch_exttimeredit_v1_0* serviceData = (Epgsearch_exttimeredit_v1_0*) Data;
      serviceData->pTimerMenu = new cMenuMyEditTimer(serviceData->timer, serviceData->bNew, serviceData->event);

      return true;
   }
   if (strcmp(Id, "Epgsearch-enablesearchtimers-v1.0") == 0) {
      if (Data == NULL)
         return true;
      else
      {
         Epgsearch_enablesearchtimers_v1_0* serviceData = (Epgsearch_enablesearchtimers_v1_0*) Data;
         if (serviceData->enable && cSearchTimerThread::m_Instance == NULL)
	   cSearchTimerThread::Init(this);
	 else if (!serviceData->enable && cSearchTimerThread::m_Instance != NULL)
	   cSearchTimerThread::Exit();
      }
      return true;
   }
   if (strcmp(Id, "Epgsearch-updatesearchtimers-v1.0") == 0) {
      if (Data == NULL)
         return true;
      else
      {
         Epgsearch_updatesearchtimers_v1_0* serviceData = (Epgsearch_updatesearchtimers_v1_0*) Data;
         if (!EPGSearchConfig.useSearchTimers) // enable search timer thread if necessary
            cSearchTimerThread::Init((cPluginEpgsearch*) cPluginManager::GetPlugin("epgsearch"), true);
         updateForced = serviceData->showMessage?3:1;
      }
      return true;
   }
   if (strcmp(Id, "Epgsearch-osdmessage-v1.0") == 0) {
      if (Data == NULL)
         return true;
      else
      {
         Epgsearch_osdmessage_v1_0* serviceData = (Epgsearch_osdmessage_v1_0*) Data;
         Skins.Message(serviceData->type, serviceData->message,5);
      }
      return true;
   }
   if (strcmp(Id, "Epgsearch-searchmenu-v1.0") == 0) {
      if (Data == NULL)
         return true;

      EpgSearchMenu_v1_0* serviceData = (EpgSearchMenu_v1_0*) Data;
      serviceData->Menu = new cMenuEPGSearchExt();

      return true;
   }
   if (strcmp(Id, "Epgsearch-conflictmenu-v1.0") == 0) {
      if (Data == NULL)
         return true;

      EpgSearchMenu_v1_0* serviceData = (EpgSearchMenu_v1_0*) Data;
      serviceData->Menu = new cMenuConflictCheck();

      return true;
   }
   if (strcmp(Id, "Epgsearch-lastconflictinfo-v1.0") == 0) {
      if (Data == NULL)
         return true;

      Epgsearch_lastconflictinfo_v1_0* serviceData = (Epgsearch_lastconflictinfo_v1_0*) Data;
      serviceData->nextConflict = cConflictCheckThread::m_cacheNextConflict;
      serviceData->relevantConflicts = cConflictCheckThread::m_cacheRelevantConflicts;
      serviceData->totalConflicts = cConflictCheckThread::m_cacheTotalConflicts;

      return true;
   }
   if (strcmp(Id, "Epgsearch-searchresults-v1.0") == 0) {
      if (Data == NULL)
         return true;
      cSearchExt* SearchExt = new cSearchExt;

      Epgsearch_searchresults_v1_0* searchData = (Epgsearch_searchresults_v1_0*) Data;
      searchData->pResultList = NULL;
      strn0cpy(SearchExt->search,searchData->query, sizeof(SearchExt->search));
      if (searchData->channelNr > 0)
      {
#if VDRVERSNUM > 20300
         LOCK_CHANNELS_READ;
         const cChannels *vdrchannels = Channels;
#else
         cChannels *vdrchannels = &Channels;
#endif
         SearchExt->useChannel = true;
         SearchExt->channelMin = vdrchannels->GetByNumber(searchData->channelNr);
         SearchExt->channelMax = vdrchannels->GetByNumber(searchData->channelNr);
      }
      SearchExt->mode = searchData->mode;
      SearchExt->useTitle = searchData->useTitle;
      SearchExt->useSubtitle = searchData->useSubTitle;
      SearchExt->useDescription = searchData->useDescription;

      cSearchResults* results = SearchExt->Run();
      // transfer to result list
      if (results)
      {
         results->SortBy(CompareEventTime);
         searchData->pResultList = new cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>;
         cSearchResult *result = results->First();
         while (result)
         {
            searchData->pResultList->Add(new Epgsearch_searchresults_v1_0::cServiceSearchResult(result->event));
            result = results->Next(result);
         }
      }
      return true;
   }
   if (strcmp(Id, "Epgsearch-switchtimer-v1.0") == 0) {
      if (Data == NULL)
         return true;
      else
      {
         Epgsearch_switchtimer_v1_0* serviceData = (Epgsearch_switchtimer_v1_0*) Data;
         if (!serviceData->event)
            return false;
         switch(serviceData->mode){
            case 0: {// query existence
               cSwitchTimer *lTimer = SwitchTimers.InSwitchList(serviceData->event);
               if (lTimer) {
                  serviceData->switchMinsBefore = lTimer->switchMinsBefore;
                  serviceData->announceOnly     = lTimer->mode;
               } // if
               serviceData->success=lTimer!=NULL;
               break;
            } // 0
            case 1: { // add/modify
               cSwitchTimer *lTimer = SwitchTimers.InSwitchList(serviceData->event);
               if (lTimer) {
                  lTimer->switchMinsBefore = serviceData->switchMinsBefore;
                  lTimer->mode             = serviceData->announceOnly;
               } else {
                  cMutexLock SwitchTimersLock(&SwitchTimers);
                  SwitchTimers.Add(new cSwitchTimer(serviceData->event,serviceData->switchMinsBefore,serviceData->announceOnly));
                  SwitchTimers.Save();
                  cSwitchTimerThread::Init();
               } // if
               serviceData->success=true;
               break;
            } // 1
            case 2: {// delete
               cSwitchTimer *lTimer = SwitchTimers.InSwitchList(serviceData->event);
               serviceData->success=lTimer!=NULL;
               if (lTimer) {
                  cMutexLock SwitchTimersLock(&SwitchTimers);
                  SwitchTimers.Del(lTimer);
                  SwitchTimers.Save();
               } // if
               break;
            } // 2
            default:
               serviceData->success=false;
               break;
         } // switch
      } // if
      return true;
   } // if
   if (strcmp(Id, "Epgsearch-quicksearch-v1.0") == 0) {
      if (Data == NULL)
         return true;

      EpgSearchMenu_v1_0* serviceData = (EpgSearchMenu_v1_0*) Data;
      serviceData->Menu = new cMenuQuickSearch(new cSearchExt);

      return true;
   }
   if (strcmp(Id, "Epgsearch-services-v1.0") == 0) {
      if (Data == NULL)
         return true;
      Epgsearch_services_v1_0* serviceData = (Epgsearch_services_v1_0*) Data;
      std::auto_ptr<cEpgsearchServiceHandler> autoHandler(new cEpgsearchServiceHandler);
      serviceData->handler = autoHandler;
      return true;
   }
   if (strcmp(Id, "Epgsearch-services-v1.1") == 0) {
      if (Data == NULL)
         return true;
      Epgsearch_services_v1_1* serviceData = (Epgsearch_services_v1_1*) Data;
      std::auto_ptr<cEpgsearchServiceHandler> autoHandler(new cEpgsearchServiceHandler);
      serviceData->handler = autoHandler;
      return true;
   }
   return false;
}
void cSearchTimerThread::Action(void)
{
   if (EPGSearchConfig.useExternalSVDRP && !cSVDRPClient::SVDRPSendCmd)
   {
      LogFile.eSysLog("ERROR - SVDRPSend script not specified or does not exist (use -f option)");
      return;
   }
   SetPriority(SEARCHTIMER_NICE);

   m_Active = true;
   // let VDR do its startup
   if (!cPluginEpgsearch::VDR_readyafterStartup)
     LogFile.Log(2, "SearchTimerThread: waiting for VDR to become ready...");
   while(Running() && m_Active && !cPluginEpgsearch::VDR_readyafterStartup)
      Wait.Wait(1000);

   time_t nextUpdate = time(NULL);
   while (m_Active && Running())
   {
      time_t now = time(NULL);
      bool needUpdate = NeedUpdate();
      if (now >= nextUpdate || needUpdate)
      {
	 justRunning = true;

	 if (updateForced & UPDS_WITH_EPGSCAN)
	 {
  	    LogFile.Log(1,"starting EPG scan before search timer update");
	    EITScanner.ForceScan();
	    do
	    {
	       Wait.Wait(1000);
	    }
	    while(EITScanner.Active() && m_Active && Running());
  	    LogFile.Log(1,"EPG scan finished");
	 }
         if (Timers.BeingEdited())
         {
            Wait.Wait(1000);
            continue;
         }
         LogFile.iSysLog("search timer update started");

         UserVars.ResetCache(); // reset internal cache of user vars
         cTimerObjList* pOutdatedTimers = NULL;

         // for thread safeness we work with a copy of the current searches,
         // because SVDRP would not work if the main thread would be locked
         cSearchExts* localSearchExts = SearchExts.Clone();
	 localSearchExts->SortBy(CompareSearchExtPrioDescTerm);
         cSearchExt *searchExt = localSearchExts->First();
         // reset announcelist
         announceList.Clear();
         while (searchExt && m_Active && Running())
         {
	   if (!searchExt->IsActiveAt(now))
            {
               searchExt = localSearchExts->Next(searchExt);
               continue;
            }
            pOutdatedTimers = searchExt->GetTimerList(pOutdatedTimers);

            cSearchResults* pSearchResults = searchExt->Run(-1, true);
            if (!pSearchResults)
            {
               searchExt = localSearchExts->Next(searchExt);
               continue;
            }
            pSearchResults->SortBy(CompareEventTime);

            if (searchExt->pauseOnNrRecordings > 0)
               searchExt->CheckExistingRecordings(pSearchResults);

            for (cSearchResult* pResultObj = pSearchResults->First();
                 pResultObj;
                 pResultObj = pSearchResults->Next(pResultObj))
            {
               if (!Running()) break;
               const cEvent* pEvent = pResultObj->event;
               if (!pEvent)
                  continue;

               cChannel *channel = Channels.GetByChannelID(pEvent->ChannelID(), true, true);
               if (!channel)
                  continue;

               int index = 0;
               cTimer *timer = new cTimer(pEvent);

               // create the file
               char* file = NULL;
               if ((file = searchExt->BuildFile(pEvent)) != NULL)
               {
                  while(strstr(file, "!^pipe^!")) file = strreplace(file, "!^pipe^!", "|"); // revert the translation of '|' in BuildFile
                  if (strstr(file, "!^invalid^!") || strlen(file) == 0)
                  {
                     LogFile.eSysLog("Skipping timer due to invalid or empty filename");
                     if (time(NULL) <= timer->StopTime())
                        pOutdatedTimers->DelTimer(timer);
                     delete timer;
                     free(file);
                     continue;
                  }
                  timer->SetFile(file);
                  free(file);
               }
               int Priority = searchExt->Priority;
               int Lifetime = searchExt->Lifetime;

               // search for an already existing timer
               bool bTimesMatchExactly = false;
               cTimer *t = GetTimer(searchExt, pEvent, bTimesMatchExactly);

               char* Summary = NULL;
	       uint timerMod = tmNoChange;

               if (t)
               { // already exists
                  pOutdatedTimers->DelTimer(t);

                  if (!t->HasFlags(tfActive))
                  { // do not update inactive timers
                     LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) not active - won't be touched", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
                     delete timer;
                     continue;
                  }

                  int triggerID = TriggeredFromSearchTimerID(t);
                  if (!pResultObj->needsTimer && !t->Recording()) // not needed
                  {
                     if (triggerID == searchExt->ID)
                     {
                        LogFile.Log(1,"delete timer for '%s~%s' (%s - %s, channel %d)", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
                        RemoveTimer(t, pEvent);
                     }
                     else if (triggerID == -1) //manual timer
                     {
                        LogFile.Log(2,"keep obsolete timer for '%s~%s' (%s - %s, channel %d) - was manually created", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
                     }
                     delete timer;
                     continue;
                  }
                  if (TimerWasModified(t)) // don't touch timer modified by user
                  {
                     LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) modified by user - won't be touched", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
                     delete timer;
                     continue;
                  }
                  if (triggerID > -1 && triggerID != searchExt->ID)
                  {
                     LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) already created by search id %d - won't be touched", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent), triggerID);
                     delete timer;
                     continue;
                  }

                  char* pFile = NULL; // File is prepared for svdrp, so prepare t->File for comparision too
                  msprintf(&pFile, "%s", t->File());
                  pFile = strreplace(pFile, ':', '|');
                  pFile = strreplace(pFile, " ~", "~");
                  pFile = strreplace(pFile, "~ ", "~");

                  Summary =  SummaryExtended(searchExt, t, pEvent);

                  if (bTimesMatchExactly && strcmp(pFile, timer->File()) == 0
                      && (t->Aux() != NULL && strcmp(t->Aux(), Summary) == 0)
                     )
                  { // dir, title, episode name and summary have not changed
                     if (Summary) free(Summary);
                     delete timer;
                     free(pFile);
                     continue;
                  }
                  else
                  {
		    if (!bTimesMatchExactly) timerMod = (uint)timerMod | tmStartStop;
		    if (strcmp(pFile, timer->File()) != 0) timerMod |= tmFile;
		    if (t->Aux() != NULL && strcmp(t->Aux(), Summary) != 0)
		      {
			char* oldEventID = GetAuxValue(t, "eventid");
			char* newEventID = GetAuxValue(Summary, "eventid");
			if (oldEventID && newEventID && strcmp(oldEventID, newEventID) != 0)
			  timerMod |= tmAuxEventID;
			free(oldEventID);
			free(newEventID);
		      }

		    if (LogFile.Level() >= 3) // output reasons for a timer modification
		      {
                        if (timerMod & tmStartStop)
			  LogFile.Log(3,"timer for '%s~%s' (%s - %s, channel %d) : start/stop has changed", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
                        if (timerMod & tmFile)
			  LogFile.Log(3,"timer for '%s~%s' (%s - %s, channel %d) : title and/or episdode has changed (old: %s, new: %s", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent), timer?timer->File():"", pFile);
                        if (timerMod & tmAuxEventID)
			  LogFile.Log(3,"timer for '%s~%s' (%s - %s, channel %d) : aux info for event id has changed", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
		      }
		    index = t->Index()+1;
		    Priority = t->Priority();
		    Lifetime = t->Lifetime();
                  }
                  free(pFile);

                  if (t->Recording() && t->StopTime() == timer->StopTime())
                  {
                     // only update recording timers if stop time has changed, since all other settings can't be modified
                     LogFile.Log(2,"timer for '%s~%s' (%s - %s, channel %d) already recording - no changes possible", pEvent->Title()?pEvent->Title():"no title", pEvent->ShortText()?pEvent->ShortText():"no subtitle", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), ChannelNrFromEvent(pEvent));
                     delete timer;
                     continue;
                  }
               }
               else
               {
                  if (!pResultObj->needsTimer)
                  {
                     delete timer;
                     continue;
                  }
               }

               if (searchExt->action == searchTimerActionAnnounceViaOSD)
               {
                  if (t || // timer already exists or
                      NoAnnounces.InList(pEvent) || // announcement not wanted anymore or
                      (EPGSearchConfig.noAnnounceWhileReplay &&
		       cDevice::PrimaryDevice()->Replaying() &&
		       !(updateForced & UPDS_WITH_OSD))  // no announce while replay within automatic updates
                     )
                  {
                     if (Summary) free(Summary);
                     delete timer;
                     continue;
                  }
		  if (!announceList.Lookup(pEvent))
		    announceList.Add(new cSearchResult(pEvent, searchExt->ID));

                  if (Summary) free(Summary);
                  delete timer;
                  continue;
               }

               if (searchExt->action == searchTimerActionAnnounceViaMail)
               {
                  if (t || // timer already exists or
                      NoAnnounces.InList(pEvent) ||
		      pEvent->StartTime() < time(NULL)) // already started?
                  {
                     if (Summary) free(Summary);
                     delete timer;
                     continue;
                  }
		  mailNotifier.AddAnnounceEventNotification(pEvent->EventID(), pEvent->ChannelID(), searchExt->ID);

                  if (Summary) free(Summary);
                  delete timer;
                  continue;
               }
               if (searchExt->action == searchTimerActionSwitchOnly ||
		   searchExt->action == searchTimerActionAnnounceAndSwitch) // add to switch list
               {
                  time_t now = time(NULL);
                  if (now < pEvent->StartTime())
                  {
                     if (!SwitchTimers.InSwitchList(pEvent))
                     {
                        cMutexLock SwitchTimersLock(&SwitchTimers);
			int mode = 0;
			if (searchExt->action == searchTimerActionAnnounceAndSwitch)
			  mode = 2;
			LogFile.Log(3,"adding switch timer event for '%s~%s' (%s - %s); search timer: '%s'", pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search);
                        SwitchTimers.Add(new cSwitchTimer(pEvent, searchExt->switchMinsBefore, mode,
							  searchExt->unmuteSoundOnSwitch));
                        SwitchTimers.Save();
                        cSwitchTimerThread::Init();
                     }
                  }
                  if (Summary) free(Summary);
                  delete timer;
                  continue;
               }

               if (AddModTimer(timer, index, searchExt, pEvent, Priority, Lifetime, Summary, timerMod))
               {
                  if (index == 0)
                     LogFile.Log(1,"added timer for '%s~%s' (%s - %s); search timer: '%s'", pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search);
                  else
                     LogFile.Log(1,"modified timer %d for '%s~%s' (%s - %s); search timer: '%s'", index, pEvent->Title(), pEvent->ShortText()?pEvent->ShortText():"", GETDATESTRING(pEvent), GETTIMESTRING(pEvent), searchExt->search);
               }
               if (Summary) free(Summary);
               delete timer;
            }
	    delete pSearchResults;
            searchExt = localSearchExts->Next(searchExt);
         }

         if (localSearchExts) delete localSearchExts;

         if (pOutdatedTimers)
         {
            if (pOutdatedTimers->Count() > 0)
            {
               LogFile.Log(1,"removing outdated timers");
               for(cTimerObj *tObj = pOutdatedTimers->First(); tObj; tObj = pOutdatedTimers->Next(tObj))
               {
                  cTimer* t = tObj->timer;
                  // timer could have been deleted meanwhile, so check if its still there
                  bool found = false;
                  for(cTimer* checkT = Timers.First(); checkT; checkT = Timers.Next(checkT))
                     if (checkT == t)
                     {
                        found = true;
                        break;
                     }
                  if (!found) continue;

                  if (TimerWasModified(t)) continue;
                  if (!t->Event()) continue; // if there is no event, we keep the timer, since EPG could have been cleared
                  if (time(NULL) > t->StopTime()) continue; // if this timer has (just) finished, let VDR do the cleanup
                  if (t->Recording()) continue; // do not remove recording timers
                  LogFile.Log(1,"delete timer for '%s' (%s, channel %s)", t->File(), DAYDATETIME(t->StartTime()), CHANNELNAME(t->Channel()));
                  RemoveTimer(t, t->Event());
               }
               LogFile.Log(1,"removing outdated timers - done");
            }
            delete pOutdatedTimers;
         }

         TimersDone.ClearOutdated();
         TimersDone.Save();

         if (announceList.Count() > 0)
         {
	   cString msgfmt = cString::sprintf(tr("%d new broadcast(s) found! Show them?"), announceList.Count());
	   if (SendMsg(msgfmt, true,7) == kOk)
	     {
	       m_plugin->showAnnounces = true;
	       cRemote::CallPlugin("epgsearch");
	     }
         }

	 CheckEPGHours();

         LogFile.iSysLog("search timer update finished");

         // check for conflicts
         if (EPGSearchConfig.checkTimerConflictsAfterUpdate && m_Active && Running())
         {
            LogFile.iSysLog("check for timer conflicts");
            cConflictCheck conflictCheck;
            conflictCheck.Check();

            if (conflictCheck.relevantConflicts > 0)
            {
               if (EPGSearchConfig.sendMailOnConflicts)
               {
                  cMailConflictNotifier mailNotifier;
                  mailNotifier.SendConflictNotifications(conflictCheck);
               }

 	       conflictCheck.EvaluateConflCheckCmd();

               cString msgfmt = cString::sprintf(tr("%d timer conflict(s)! First at %s. Show them?"),
						 conflictCheck.relevantConflicts,
						 *DateTime(conflictCheck.nextRelevantConflictDate));
               bool doMessage = EPGSearchConfig.noConflMsgWhileReplay == 0 ||
                  !cDevice::PrimaryDevice()->Replaying() ||
                  conflictCheck.nextRelevantConflictDate - now < 2*60*60 ||
		 (updateForced & UPDS_WITH_OSD);
               if (doMessage && SendMsg(msgfmt, true,7) == kOk)
               {
                  m_plugin->showConflicts = true;
                  cRemote::CallPlugin("epgsearch");
               }
            }

            LogFile.iSysLog("check for timer conflicts - done");
         }

         // delete expired recordings
         CheckExpiredRecs();

         // check for updates for manual timers
         CheckManualTimers();

	 if (m_Active)
	   mailNotifier.SendUpdateNotifications();

         if ((updateForced & UPDS_WITH_OSD) && m_Active)
            SendMsg(tr("Search timer update done!"));

         // reset service call flag
         updateForced = 0;

         m_lastUpdate = time(NULL);
         nextUpdate = long(m_lastUpdate/60)*60 + (EPGSearchConfig.UpdateIntervall * 60);
	 justRunning = false;
      }
      if (m_Active && Running())
	Wait.Wait(2000); // to avoid high system load if time%30==0
      while (Running() && m_Active && !NeedUpdate() && time(NULL)%30 != 0) // sync heart beat to a multiple of 5secs
         Wait.Wait(1000);
   };
   LogFile.iSysLog("Leaving search timer thread");
}