All pastes #1961433 Raw Edit

Untitled

public text v1 · immutable
#1961433 ·published 2010-10-14 00:40 UTC
rendered paste body
Index: programs/mythbackend/scheduler.h
===================================================================
--- programs/mythbackend/scheduler.h	(revision 26756)
+++ programs/mythbackend/scheduler.h	(working copy)
@@ -148,6 +148,25 @@
                          const RecList &reclist);
     void FillDirectoryInfoCache(bool force = false);
 
+    int CalcTimeToNextHandleRecordingEvent(
+        const QDateTime &curtime,
+        RecConstIter startIter, const RecList &reclist,
+        int prerollseconds, int max_sleep /*ms*/);
+    bool HandleReschedule(void);
+    bool HandleRunSchedulerStartup(
+        int prerollseconds, int idleWaitForRecordingTime);
+    void HandleWakeSlave(RecordingInfo &ri, int prerollseconds);
+    bool HandleRecording(
+        RecordingInfo &ri, bool &statuschanged, int prerollseconds);
+    void HandleTuning(
+        RecordingInfo &ri, bool &statuschanged);
+    void HandleRecordingStatusChange(
+        RecordingInfo &ri, RecStatusTypes recStatus, const QString &details);
+    void HandleIdleShutdown(
+        bool &blockShutdown, QDateTime &idleSince, int prerollseconds,
+        int idleTimeoutSecs, int idleWaitForRecordingTime);
+
+
     MythDeque<int> reschedQueue;
     QMutex reschedLock;
     QMutex recordmatchLock;
Index: programs/mythbackend/scheduler.cpp
===================================================================
--- programs/mythbackend/scheduler.cpp	(revision 26756)
+++ programs/mythbackend/scheduler.cpp	(working copy)
@@ -17,6 +17,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <cmath>
 
 #include <QStringList>
 #include <QDateTime>
@@ -214,6 +215,7 @@
 static inline bool Recording(const RecordingInfo *p)
 {
     return (p->GetRecordingStatus() == rsRecording ||
+            p->GetRecordingStatus() == rsTuning ||
             p->GetRecordingStatus() == rsWillRecord);
 }
 
@@ -289,8 +291,10 @@
 
 static bool comp_priority(RecordingInfo *a, RecordingInfo *b)
 {
-    int arec = (a->GetRecordingStatus() != rsRecording);
-    int brec = (b->GetRecordingStatus() != rsRecording);
+    int arec = (a->GetRecordingStatus() != rsRecording &&
+                a->GetRecordingStatus() != rsTuning);
+    int brec = (b->GetRecordingStatus() != rsRecording &&
+                b->GetRecordingStatus() != rsTuning);
 
     if (arec != brec)
         return arec < brec;
@@ -644,14 +648,14 @@
                 if (sp->GetCardID() == rp->GetCardID())
                 {
                     found = true;
-                    rp->SetRecordingStatus(rsRecording);
+                    rp->SetRecordingStatus(sp->GetRecordingStatus());
                     reclist_changed = true;
                     rp->AddHistory(false);
-                    VERBOSE(VB_IMPORTANT, QString("setting %1/%2/\"%3\" as "
-                                                  "recording")
+                    VERBOSE(VB_IMPORTANT, QString("setting %1/%2/\"%3\" as %4")
                             .arg(sp->GetCardID())
                             .arg(sp->GetChannelSchedulingID())
-                            .arg(sp->GetTitle()));
+                            .arg(sp->GetTitle())
+                            .arg(toUIState(sp->GetRecordingStatus())));
                 }
                 else
                 {
@@ -664,7 +668,8 @@
                 }
             }
             else if (sp->GetCardID() == rp->GetCardID() &&
-                     rp->GetRecordingStatus() == rsRecording)
+                     (rp->GetRecordingStatus() == rsRecording ||
+                      rp->GetRecordingStatus() == rsTuning))
             {
                 rp->SetRecordingStatus(rsAborted);
                 reclist_changed = true;
@@ -699,7 +704,8 @@
         RecordingInfo *rp = *ri;
 
         if (rp->GetCardID() == cardid &&
-            rp->GetRecordingStatus() == rsRecording)
+            (rp->GetRecordingStatus() == rsRecording ||
+             rp->GetRecordingStatus() == rsTuning))
         {
             rp->SetRecordingStatus(rsAborted);
             reclist_changed = true;
@@ -720,7 +726,8 @@
     for (; i != reclist.end(); ++i)
     {
         RecordingInfo *p = *i;
-        if (p->GetRecordingStatus() == rsRecording)
+        if (p->GetRecordingStatus() == rsRecording ||
+            p->GetRecordingStatus() == rsTuning)
             worklist.push_back(new RecordingInfo(*p));
     }
 }
@@ -806,6 +813,7 @@
     {
         RecordingInfo *p = *i;
         if (p->GetRecordingStatus() == rsRecording ||
+            p->GetRecordingStatus() == rsTuning ||
             p->GetRecordingStatus() == rsWillRecord ||
             p->GetRecordingStatus() == rsUnknown)
         {
@@ -1024,7 +1032,8 @@
 {
     PrintRec(p, "     >");
 
-    if (p->GetRecordingStatus() == rsRecording)
+    if (p->GetRecordingStatus() == rsRecording ||
+        p->GetRecordingStatus() == rsTuning)
         return false;
 
     RecList *showinglist = &titlelistmap[p->GetTitle()];
@@ -1171,7 +1180,8 @@
     while (i != worklist.end())
     {
         RecordingInfo *p = *i;
-        if (p->GetRecordingStatus() == rsRecording)
+        if (p->GetRecordingStatus() == rsRecording ||
+            p->GetRecordingStatus() == rsTuning)
             MarkOtherShowings(p);
         else if (p->GetRecordingStatus() == rsUnknown)
         {
@@ -1304,6 +1314,7 @@
         // Delete anything that has already passed since we can't
         // change history, can we?
         if (p->GetRecordingStatus() != rsRecording &&
+            p->GetRecordingStatus() != rsTuning &&
             p->GetScheduledEndTime() < schedTime &&
             p->GetRecordingEndTime() < schedTime)
         {
@@ -1478,7 +1489,8 @@
     RecConstIter it = reclist.begin();
     for (; it != reclist.end(); ++it)
     {
-        if (rsRecording == (*it)->GetRecordingStatus())
+        if (rsRecording == (*it)->GetRecordingStatus() ||
+            rsTuning == (*it)->GetRecordingStatus())
             recMap[(*it)->MakeUniqueKey()] = new ProgramInfo(**it);
     }
 
@@ -1493,8 +1505,9 @@
     {
         if (pginfo.IsSameRecording(**it))
         {
-            return (rsRecording == (**it).GetRecordingStatus()) ?
-                rsRecording : pginfo.GetRecordingStatus();
+            return (rsRecording == (**it).GetRecordingStatus() ||
+                    rsTuning == (**it).GetRecordingStatus()) ?
+                (**it).GetRecordingStatus() : pginfo.GetRecordingStatus();
         }
     }
 
@@ -1630,41 +1643,14 @@
 
 void Scheduler::RunScheduler(void)
 {
-    int prerollseconds = 0;
-    int wakeThreshold = gCoreContext->GetNumSetting("WakeUpThreshold", 300);
-    int secsleft = 0;
-    EncoderLink *nexttv = NULL;
-
-    RecordingInfo *nextRecording = NULL;
-    QDateTime nextrectime;
-    QString schedid;
-
-    QDateTime curtime;
-    QDateTime lastupdate = QDateTime::currentDateTime().addDays(-1);
-    QDateTime lastSleepCheck = QDateTime::currentDateTime().addDays(-1);
-
-    RecIter startIter = reclist.begin();
-
-    bool blockShutdown = gCoreContext->GetNumSetting("blockSDWUwithoutClient", 1);
-    QDateTime idleSince = QDateTime();
-    int idleTimeoutSecs = 0;
-    int idleWaitForRecordingTime = 0;
-    bool firstRun = true;
-
-    QString sysEventKey;
-    int sysEventSecs[5] = { 120, 90, 60, 30, 0 };
-    QList<QString>sysEvents[4];
-
-    struct timeval fillstart, fillend;
-    float matchTime, placeTime;
-
     // Mark anything that was recording as aborted.  We'll fix it up.
     // if possible, after the slaves connect and we start scheduling.
     MSqlQuery query(dbConn);
     query.prepare("UPDATE oldrecorded SET recstatus = :RSABORTED "
-                  "  WHERE recstatus = :RSRECORDING");
+                  "  WHERE recstatus = :RSRECORDING OR recstatus = :RSTUNING");
     query.bindValue(":RSABORTED", rsAborted);
     query.bindValue(":RSRECORDING", rsRecording);
+    query.bindValue(":RSTUNING", rsTuning);
     if (!query.exec())
         MythDB::DBError("UpdateAborted", query);
 
@@ -1673,603 +1659,807 @@
 
     Reschedule(-1);
 
-    while (1)
+    int       prerollseconds  = 0;
+    int       wakeThreshold   = 300;
+    int       idleTimeoutSecs = 0;
+    int       idleWaitForRecordingTime = 15; // in minutes
+    bool      blockShutdown   =
+        gCoreContext->GetNumSetting("blockSDWUwithoutClient", 1);
+    bool      firstRun        = true;
+    QDateTime lastSleepCheck  = QDateTime::currentDateTime().addDays(-1);
+    RecIter   startIter       = reclist.begin();
+    QDateTime idleSince       = QDateTime();
+    int       maxSleep        = 60000; // maximum sleep time in milliseconds
+    int       schedRunTime    = 30; // max scheduler run time in seconds
+
+    while (true)
     {
-        curtime = QDateTime::currentDateTime();
+        QDateTime curtime = QDateTime::currentDateTime();
         bool statuschanged = false;
+        int secs_to_next = (startIter != reclist.end()) ?
+            curtime.secsTo((*startIter)->GetRecordingStartTime()) : 60*60;
 
-        if ((startIter != reclist.end() &&
-             curtime.secsTo((*startIter)->GetRecordingStartTime()) < 30))
-            sleep(1);
+        // If we're about to start a recording don't do any reschedules...
+        // instead sleep for a bit
+        if (secs_to_next < (schedRunTime + 2))
+        {
+            int msecs = CalcTimeToNextHandleRecordingEvent(
+                curtime, startIter, reclist, prerollseconds, maxSleep);
+            if (msecs > 200)
+                (void) ::usleep(msecs * 1000);
+        }
         else
         {
+            int sched_sleep = (secs_to_next - schedRunTime - 1) * 1000;
+            sched_sleep = min(sched_sleep, maxSleep);
             reschedLock.lock();
             if (reschedQueue.empty())
-                reschedWait.wait(&reschedLock, 1000);
+                reschedWait.wait(&reschedLock, sched_sleep);
             bool queue_empty = reschedQueue.empty();
             reschedLock.unlock();
 
-            if (!queue_empty)
+            QTime t; t.start();
+            if (!queue_empty && HandleReschedule())
             {
-                // We might have been inactive for a long time, so make
-                // sure our DB connection is fresh before continuing.
-                dbConn = MSqlQuery::SchedCon();
+                statuschanged = true;
+                startIter = reclist.begin();
 
-                gettimeofday(&fillstart, NULL);
-                QString msg;
+                // The master backend is a long lived program, so
+                // we reload some key settings on each reschedule.
+                prerollseconds  =
+                    gCoreContext->GetNumSetting("RecordPreRoll", 0);
+                wakeThreshold =
+                    gCoreContext->GetNumSetting("WakeUpThreshold", 300);
+                idleTimeoutSecs =
+                    gCoreContext->GetNumSetting("idleTimeoutSecs", 0);
+                idleWaitForRecordingTime =
+                    gCoreContext->GetNumSetting("idleWaitForRecordingTime", 15);
+            }
+            int e = t.elapsed();
+            if (e > 0)
+            {
+                schedRunTime = (firstRun) ? 0 : schedRunTime;
+                schedRunTime =
+                    max((int) ceil((e + 999 / 1000) * 1.5f), schedRunTime);
+                VERBOSE(VB_IMPORTANT, QString("schedRunTime: %1 seconds")
+                        .arg(schedRunTime));
+            }
 
-                reschedLock.lock();
-                while (!reschedQueue.empty())
-                {
-                    int recordid = reschedQueue.dequeue();
-                    reschedLock.unlock();
+            if (firstRun)
+            {
+                blockShutdown &= HandleRunSchedulerStartup(
+                    prerollseconds, idleWaitForRecordingTime);
+                firstRun = false;
+            }
 
-                    VERBOSE(VB_GENERAL,
-                            QString("Reschedule requested for id %1.")
-                            .arg(recordid));
+            // Unless a recording is about to start, check for slaves
+            // that can be put to sleep if it has been at least five
+            // minutes since we last put slaves to sleep.
+            curtime = QDateTime::currentDateTime();
+            secs_to_next = (startIter != reclist.end()) ?
+                curtime.secsTo((*startIter)->GetRecordingStartTime()) : 60*60;
+            if ((secs_to_next > schedRunTime * 1.5f) &&
+                (lastSleepCheck.secsTo(curtime) > 300))
+            {
+                PutInactiveSlavesToSleep();
+                lastSleepCheck = QDateTime::currentDateTime();
+            }
+        }
 
-                    if (recordid != 0)
-                    {
-                        if (recordid == -1)
-                        {
-                            reschedLock.lock();
-                            reschedQueue.clear();
-                            reschedLock.unlock();
-                        }
+        // Skip past recordings that are already history
+        // (i.e. AddHistory() has been called setting oldrecstatus)
+        for ( ; startIter != reclist.end(); ++startIter)
+        {
+            if ((*startIter)->GetRecordingStatus() !=
+                (*startIter)->oldrecstatus)
+            {
+                break;
+            }
+        }
 
-                        QMutexLocker locker(&recordmatchLock);
-                        UpdateMatches(recordid);
-                    }
+        // Start any recordings that are due to be started
+        // & call RecordPending for recordings due to start in 30 seconds
+        // & handle rsTuning updates
+        bool done = false;
+        for (RecIter it = startIter; it != reclist.end() && !done; ++it)
+            done = HandleRecording(**it, statuschanged, prerollseconds);
 
-                    reschedLock.lock();
-                }
-                reschedLock.unlock();
+        /// Wake any slave backends that need waking
+        curtime = QDateTime::currentDateTime();
+        for (RecIter it = startIter; it != reclist.end() && !done; ++it)
+        {
+            int secsleft = curtime.secsTo((*it)->GetRecordingStartTime());
+            if ((secsleft - prerollseconds) <= wakeThreshold)
+                HandleWakeSlave(**it, prerollseconds);
+            else
+                break;
+        }
 
-                gettimeofday(&fillend, NULL);
+        if (statuschanged)
+        {
+            MythEvent me("SCHEDULE_CHANGE");
+            gCoreContext->dispatch(me);
+            idleSince = QDateTime();
+        }
 
-                matchTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 +
-                             (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0;
+        // if idletimeout is 0, the user disabled the auto-shutdown feature
+        if ((idleTimeoutSecs > 0) && (m_mainServer != NULL))
+        {
+            HandleIdleShutdown(blockShutdown, idleSince, prerollseconds,
+                               idleTimeoutSecs, idleWaitForRecordingTime);
+        }
+    }
+}
 
-                gettimeofday(&fillstart, NULL);
-                bool worklistused = FillRecordList();
-                gettimeofday(&fillend, NULL);
-                if (worklistused)
-                {
-                    UpdateNextRecord();
-                    PrintList();
-                }
-                else
-                {
-                    VERBOSE(VB_GENERAL, "Reschedule interrupted, will retry");
-                    Reschedule(0);
-                    continue;
-                }
+int Scheduler::CalcTimeToNextHandleRecordingEvent(
+    const QDateTime &curtime,
+    RecConstIter startIter, const RecList &reclist,
+    int prerollseconds, int max_sleep /*ms*/)
+{
+    if (startIter == reclist.end())
+        return max_sleep;
 
-                placeTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 +
-                             (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0;
+    int msecs = max_sleep;
+    for (RecConstIter i = startIter; i != reclist.end() && (msecs > 0); ++i)
+    {
+        // Check on recordings that we've told to start, but have
+        // not yet started every second or so.
+        if ((*i)->GetRecordingStatus() == rsTuning)
+        {
+            msecs = min(msecs, 1000);
+            continue;
+        }
 
-                msg.sprintf("Scheduled %d items in "
-                            "%.1f = %.2f match + %.2f place",
-                            (int)reclist.size(),
-                            matchTime + placeTime, matchTime, placeTime);
+        // These recordings have already been handled..
+        if ((*i)->GetRecordingStatus() == (*i)->oldrecstatus)
+            continue;
 
-                VERBOSE(VB_GENERAL, msg);
-                gCoreContext->LogEntry("scheduler", LP_INFO,
-                                       "Scheduled items", msg);
+        int secs_to_next = curtime.secsTo((*i)->GetRecordingStartTime());
 
-                fsInfoCacheFillTime =
-                    QDateTime::currentDateTime().addSecs(-1000);
+        if (!recPendingList[(*i)->MakeUniqueSchedulerKey()])
+            secs_to_next -= 30;
 
-                lastupdate = curtime;
-                startIter = reclist.begin();
-                statuschanged = true;
+        if (secs_to_next < 0)
+        {
+            msecs = 0;
+            break;
+        }
 
-                // Determine if the user wants us to start recording early
-                // and by how many seconds
-                prerollseconds = gCoreContext->GetNumSetting("RecordPreRoll");
+        // This is what normally breaks us out of the loop...
+        if (secs_to_next > max_sleep)
+        {
+            msecs = min(msecs, max_sleep);
+            break;
+        }
 
-                idleTimeoutSecs =
-                    gCoreContext->GetNumSetting("idleTimeoutSecs", 0);
-                idleWaitForRecordingTime =
-                    gCoreContext->GetNumSetting("idleWaitForRecordingTime", 15);
+        if (secs_to_next > 31)
+        {
+            msecs = min(msecs, 30 * 1000);
+            continue;
+        }
 
-                if (firstRun)
-                {
-                    //the parameter given to the startup_cmd. "user" means a user
-                    // started the BE, 'auto' means it was started automatically
-                    QString startupParam = "user";
+        if ((secs_to_next-1) * 1000 > msecs)
+            continue;
 
-                    // find the first recording that WILL be recorded
-                    RecIter firstRunIter = reclist.begin();
-                    for ( ; firstRunIter != reclist.end(); ++firstRunIter)
-                        if ((*firstRunIter)->GetRecordingStatus() == rsWillRecord)
-                            break;
+        if (secs_to_next < 15)
+        {
+            QTime st = (*i)->GetRecordingStartTime().time();
+            int tmp = curtime.time().msecsTo(st);
+            tmp = (tmp < 0) ? tmp + 86400000 : tmp;
+            msecs = (tmp > 15*1000) ? 0 : min(msecs, tmp);
+        }
+        else
+        {
+            msecs = min(msecs, (secs_to_next-1) * 1000);
+        }
+    }
 
-                    // have we been started automatically?
-                    if (WasStartedAutomatically() ||
-                        ((firstRunIter != reclist.end()) &&
-                         ((curtime.secsTo((*firstRunIter)->GetRecordingStartTime()) - prerollseconds)
-                          < (idleWaitForRecordingTime * 60))))
-                    {
-                        VERBOSE(VB_IMPORTANT, "AUTO-Startup assumed");
-                        startupParam = "auto";
+    return min(msecs, max_sleep);
+}
 
-                        // Since we've started automatically, don't wait for
-                        // client to connect before allowing shutdown.
-                        blockShutdown = false;
-                    }
-                    else
-                    {
-                        VERBOSE(VB_IMPORTANT, "Seem to be woken up by USER");
-                    }
+bool Scheduler::HandleReschedule(void)
+{
+    // We might have been inactive for a long time, so make
+    // sure our DB connection is fresh before continuing.
+    dbConn = MSqlQuery::SchedCon();
 
-                    QString startupCommand =
-                        gCoreContext->GetSetting("startupCommand", "");
-                    if (!startupCommand.isEmpty())
-                    {
-                        startupCommand.replace("$status", startupParam);
-                        myth_system(startupCommand);
-                    }
-                    firstRun = false;
-                }
+    struct timeval fillstart;
+    gettimeofday(&fillstart, NULL);
+    QString msg;
 
-                PutInactiveSlavesToSleep();
-                lastSleepCheck = QDateTime::currentDateTime();
+    reschedLock.lock();
+    while (!reschedQueue.empty())
+    {
+        int recordid = reschedQueue.dequeue();
+        reschedLock.unlock();
 
-                SendMythSystemEvent("SCHEDULER_RAN");
+        VERBOSE(VB_GENERAL,
+                QString("Reschedule requested for id %1.")
+                .arg(recordid));
+
+        if (recordid != 0)
+        {
+            if (recordid == -1)
+            {
+                reschedLock.lock();
+                reschedQueue.clear();
+                reschedLock.unlock();
             }
+
+            QMutexLocker locker(&recordmatchLock);
+            UpdateMatches(recordid);
         }
 
-        for ( ; startIter != reclist.end(); ++startIter)
-            if ((*startIter)->GetRecordingStatus() !=
-                (*startIter)->oldrecstatus)
-                break;
+        reschedLock.lock();
+    }
+    reschedLock.unlock();
 
-        curtime = QDateTime::currentDateTime();
+    struct timeval fillend;
+    gettimeofday(&fillend, NULL);
 
-        // About every 5 minutes check for slaves that can be put to sleep
-        if (lastSleepCheck.secsTo(curtime) > 300)
-        {
-            PutInactiveSlavesToSleep();
-            lastSleepCheck = QDateTime::currentDateTime();
-        }
+    float matchTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 +
+                       (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0;
 
-        // Go through the list of recordings starting in the next few minutes
-        // and wakeup any slaves that are asleep
-        RecIter recIter = startIter;
-        for ( ; schedulingEnabled && recIter != reclist.end(); ++recIter)
-        {
-            nextRecording = *recIter;
-            nextrectime = nextRecording->GetRecordingStartTime();
-            secsleft = curtime.secsTo(nextrectime);
+    gettimeofday(&fillstart, NULL);
+    bool worklistused = FillRecordList();
+    gettimeofday(&fillend, NULL);
+    if (worklistused)
+    {
+        UpdateNextRecord();
+        PrintList();
+    }
+    else
+    {
+        VERBOSE(VB_GENERAL, "Reschedule interrupted, will retry");
+        Reschedule(0);
+        return false;
+    }
 
-            if ((secsleft - prerollseconds) > wakeThreshold)
-                break;
+    float placeTime = ((fillend.tv_sec - fillstart.tv_sec ) * 1000000 +
+                       (fillend.tv_usec - fillstart.tv_usec)) / 1000000.0;
 
-            if (m_tvList->find(nextRecording->GetCardID()) == m_tvList->end())
-                continue;
+    msg.sprintf("Scheduled %d items in "
+                "%.1f = %.2f match + %.2f place",
+                (int)reclist.size(),
+                matchTime + placeTime, matchTime, placeTime);
 
-            sysEventKey = QString("%1:%2").arg(nextRecording->GetChanID())
-                          .arg(nextrectime.toString(Qt::ISODate));
-            int i = 0;
-            bool pendingEventSent = false;
-            while (sysEventSecs[i] != 0)
-            {
-                if ((secsleft <= sysEventSecs[i]) &&
-                    (!sysEvents[i].contains(sysEventKey)))
-                {
-                    if (!pendingEventSent)
-                        SendMythSystemRecEvent
-                            (QString("REC_PENDING SECS %1").arg(secsleft),
-                             nextRecording);
+    VERBOSE(VB_GENERAL, msg);
+    gCoreContext->LogEntry("scheduler", LP_INFO,
+                           "Scheduled items", msg);
 
-                    sysEvents[i].append(sysEventKey);
-                    pendingEventSent = true;
-                }
-                i++;
-            }
+    fsInfoCacheFillTime =
+        QDateTime::currentDateTime().addSecs(-1000);
 
-            nexttv = (*m_tvList)[nextRecording->GetCardID()];
+    SendMythSystemEvent("SCHEDULER_RAN");
 
-            if (nexttv->IsAsleep() && !nexttv->IsWaking())
-            {
-                VERBOSE(VB_SCHEDULE, QString("Slave Backend %1 is being "
-                                             "awakened to record: %2")
-                        .arg(nexttv->GetHostName())
-                        .arg(nextRecording->GetTitle()));
+    return true;
+}
 
-                if (!WakeUpSlave(nexttv->GetHostName()))
-                {
-                    Reschedule(0);
-                    continue;
-                }
-            }
-            else if ((nexttv->IsWaking()) &&
-                     ((secsleft - prerollseconds) < 210) &&
-                     (nexttv->GetSleepStatusTime().secsTo(curtime) < 300) &&
-                     (nexttv->GetLastWakeTime().secsTo(curtime) > 10))
+bool Scheduler::HandleRunSchedulerStartup(
+    int prerollseconds, int idleWaitForRecordingTime)
+{
+    bool blockShutdown = true;
+
+    // The parameter given to the startup_cmd. "user" means a user
+    // probably started the backend process, "auto" means it was
+    // started probably automatically.
+    QString startupParam = "user";
+
+    // find the first recording that WILL be recorded
+    RecIter firstRunIter = reclist.begin();
+    for ( ; firstRunIter != reclist.end(); ++firstRunIter)
+    {
+        if ((*firstRunIter)->GetRecordingStatus() == rsWillRecord)
+            break;
+    }
+
+    // have we been started automatically?
+    QDateTime curtime = QDateTime::currentDateTime();
+    if (WasStartedAutomatically() ||
+        ((firstRunIter != reclist.end()) &&
+         ((curtime.secsTo((*firstRunIter)->GetRecordingStartTime()) -
+           prerollseconds) < (idleWaitForRecordingTime * 60))))
+    {
+        VERBOSE(VB_GENERAL, LOC + "AUTO-Startup assumed");
+        startupParam = "auto";
+
+        // Since we've started automatically, don't wait for
+        // client to connect before allowing shutdown.
+        blockShutdown = false;
+    }
+    else
+    {
+        VERBOSE(VB_GENERAL, LOC + "Seem to be woken up by USER");
+    }
+
+    QString startupCommand = gCoreContext->GetSetting("startupCommand", "");
+    if (!startupCommand.isEmpty())
+    {
+        startupCommand.replace("$status", startupParam);
+        myth_system(startupCommand);
+    }
+
+    return blockShutdown;
+}
+
+// If a recording is about to start on a backend in a few minutes, wake it...
+void Scheduler::HandleWakeSlave(RecordingInfo &ri, int prerollseconds)
+{
+    static const int sysEventSecs[5] = { 120, 90, 60, 30, 0 };
+    QString sysEventKey;
+    QList<QString> sysEvents[4];
+
+    QDateTime curtime = QDateTime::currentDateTime();
+    QDateTime nextrectime = ri.GetRecordingStartTime();
+    int secsleft = curtime.secsTo(nextrectime);
+
+    QMap<int, EncoderLink*>::iterator tvit = m_tvList->find(ri.GetCardID());
+    if (tvit == m_tvList->end())
+        return;
+
+    sysEventKey = QString("%1:%2").arg(ri.GetChanID())
+        .arg(nextrectime.toString(Qt::ISODate));
+
+    int i = 0;
+    bool pendingEventSent = false;
+    while (sysEventSecs[i] != 0)
+    {
+        if ((secsleft <= sysEventSecs[i]) &&
+            (!sysEvents[i].contains(sysEventKey)))
+        {
+            if (!pendingEventSent)
             {
-                VERBOSE(VB_SCHEDULE, QString("Slave Backend %1 not "
-                                             "available yet, trying to wake it up again.")
-                        .arg(nexttv->GetHostName()));
-                if (!WakeUpSlave(nexttv->GetHostName(), false))
-                {
-                    Reschedule(0);
-                    continue;
-                }
+                SendMythSystemRecEvent(
+                    QString("REC_PENDING SECS %1").arg(secsleft), &ri);
             }
-            else if ((nexttv->IsWaking()) &&
-                     ((secsleft - prerollseconds) < 150) &&
-                     (nexttv->GetSleepStatusTime().secsTo(curtime) < 300))
-            {
-                VERBOSE(VB_SCHEDULE,
-                        QString("WARNING: Slave Backend %1 has NOT come "
-                                "back from sleep yet in 150 seconds. Setting "
-                                "slave status to unknown and attempting "
-                                "to reschedule around its tuners.")
-                        .arg(nexttv->GetHostName()));
 
-                QMap<int, EncoderLink *>::Iterator enciter =
-                    m_tvList->begin();
-                for (; enciter != m_tvList->end(); ++enciter)
-                {
-                    EncoderLink *enc = *enciter;
-                    if (enc->GetHostName() == nexttv->GetHostName())
-                        enc->SetSleepStatus(sStatus_Undefined);
-                }
+            sysEvents[i].append(sysEventKey);
+            pendingEventSent = true;
+        }
+        i++;
+    }
 
-                Reschedule(0);
-            }
+    EncoderLink *nexttv = *tvit;
+
+    if (nexttv->IsAsleep() && !nexttv->IsWaking())
+    {
+        VERBOSE(VB_SCHEDULE, LOC +
+                QString("Slave Backend %1 is being awakened to record: %2")
+                .arg(nexttv->GetHostName()).arg(ri.GetTitle()));
+
+        if (!WakeUpSlave(nexttv->GetHostName()))
+            Reschedule(0);
+    }
+    else if ((nexttv->IsWaking()) &&
+             ((secsleft - prerollseconds) < 210) &&
+             (nexttv->GetSleepStatusTime().secsTo(curtime) < 300) &&
+             (nexttv->GetLastWakeTime().secsTo(curtime) > 10))
+    {
+        VERBOSE(VB_SCHEDULE, LOC +
+                QString("Slave Backend %1 not available yet, "
+                        "trying to wake it up again.")
+                .arg(nexttv->GetHostName()));
+
+        if (!WakeUpSlave(nexttv->GetHostName(), false))
+            Reschedule(0);
+    }
+    else if ((nexttv->IsWaking()) &&
+             ((secsleft - prerollseconds) < 150) &&
+             (nexttv->GetSleepStatusTime().secsTo(curtime) < 300))
+    {
+        VERBOSE(VB_GENERAL, LOC_WARN +
+                QString("Slave Backend %1 has NOT come "
+                        "back from sleep yet in 150 seconds. Setting "
+                        "slave status to unknown and attempting "
+                        "to reschedule around its tuners.")
+                .arg(nexttv->GetHostName()));
+
+        QMap<int, EncoderLink*>::iterator it = m_tvList->begin();
+        for (; it != m_tvList->end(); ++it)
+        {
+            if ((*it)->GetHostName() == nexttv->GetHostName())
+                (*it)->SetSleepStatus(sStatus_Undefined);
         }
 
-        for ( recIter = startIter ; recIter != reclist.end(); ++recIter)
+        Reschedule(0);
+    }
+}
+
+bool Scheduler::HandleRecording(
+    RecordingInfo &ri, bool &statuschanged, int prerollseconds)
+{
+    if (ri.GetRecordingStatus() == rsTuning)
+    {
+        HandleTuning(ri, statuschanged);
+        return false;
+    }
+
+    if (ri.GetRecordingStatus() != rsWillRecord)
+    {
+        if (ri.GetRecordingStatus() != ri.oldrecstatus &&
+            ri.GetRecordingStartTime() <= QDateTime::currentDateTime())
         {
-            QString msg, details;
-            int fsID = -1;
+            ri.AddHistory(false);
+        }
+        return false;
+    }
 
-            nextRecording = *recIter;
+    QDateTime nextrectime = ri.GetRecordingStartTime();
+    QDateTime curtime     = QDateTime::currentDateTime();
+    int       secsleft    = curtime.secsTo(nextrectime);
+    QString   schedid     = ri.MakeUniqueSchedulerKey();
 
-            if (nextRecording->GetRecordingStatus() != rsTuning)
-            {
-                if (nextRecording->GetRecordingStatus() != rsWillRecord)
-                {
-                    if (nextRecording->GetRecordingStatus() !=
-                        nextRecording->oldrecstatus &&
-                        nextRecording->GetRecordingStartTime() <= curtime)
-                        nextRecording->AddHistory(false);
-                    continue;
-                }
+    if (secsleft - prerollseconds < 60)
+    {
+        if (!recPendingList.contains(schedid))
+        {
+            recPendingList[schedid] = false;
+            
+            livetvTime = (livetvTime < nextrectime) ?
+                nextrectime : livetvTime;
 
-                nextrectime = nextRecording->GetRecordingStartTime();
-                secsleft = curtime.secsTo(nextrectime);
-                schedid = nextRecording->MakeUniqueSchedulerKey();
+            Reschedule(0);
+        }
+    }
 
-                if (secsleft - prerollseconds < 60)
-                {
-                    if (!recPendingList.contains(schedid))
-                    {
-                        recPendingList[schedid] = false;
+    if (secsleft - prerollseconds > 35)
+        return true;
 
-                        livetvTime = (livetvTime < nextrectime) ?
-                                     nextrectime : livetvTime;
+    QMap<int, EncoderLink*>::iterator tvit = m_tvList->find(ri.GetCardID());
+    if (tvit == m_tvList->end())
+    {
+        QString msg = QString("Invalid cardid (%1) for %2")
+            .arg(ri.GetCardID()).arg(ri.GetTitle());
+        VERBOSE(VB_GENERAL, LOC + msg);
 
-                        Reschedule(0);
-                    }
-                }
+        QMutexLocker lockit(reclist_lock);
+        ri.SetRecordingStatus(rsTunerBusy);
+        ri.AddHistory(true);
+        statuschanged = true;
+        return false;
+    }
 
-                if (secsleft - prerollseconds > 35)
-                    break;
-            }
+    EncoderLink *nexttv = *tvit;
 
-            if (m_tvList->find(nextRecording->GetCardID()) == m_tvList->end())
-            {
-                msg = QString("invalid cardid (%1) for %2")
-                      .arg(nextRecording->GetCardID())
-                      .arg(nextRecording->GetTitle());
-                VERBOSE(VB_GENERAL, msg);
+    if (nexttv->IsTunerLocked())
+    {
+        QString msg = QString("SUPPRESSED recording \"%1\" on channel: "
+                              "%2 on cardid: %3, sourceid %4. Tuner "
+                              "is locked by an external application.")
+            .arg(ri.GetTitle())
+            .arg(ri.GetChanID())
+            .arg(ri.GetCardID())
+            .arg(ri.GetSourceID());
+        QByteArray amsg = msg.toLocal8Bit();
+        VERBOSE(VB_GENERAL, msg.constData());
 
-                QMutexLocker lockit(reclist_lock);
-                nextRecording->SetRecordingStatus(rsTunerBusy);
-                nextRecording->AddHistory(true);
-                statuschanged = true;
-                continue;
-            }
+        QMutexLocker lockit(reclist_lock);
+        ri.SetRecordingStatus(rsTunerBusy);
+        ri.AddHistory(true);
+        statuschanged = true;
+        return false;
+    }
 
-            nexttv = (*m_tvList)[nextRecording->GetCardID()];
-            // cerr << "nexttv = " << nextRecording->GetCardID();
-            // cerr << " title: " << nextRecording->GetTitle() << endl;
+    if ((prerollseconds > 0) && !IsBusyRecording(&ri))
+    {
+        // Will use pre-roll settings only if no other
+        // program is currently being recorded
+        secsleft -= prerollseconds;
+    }
 
-            if (nextRecording->GetRecordingStatus() != rsTuning)
-            {
-                if (nexttv->IsTunerLocked())
-                {
-                    msg = QString("SUPPRESSED recording \"%1\" on channel: "
-                                  "%2 on cardid: %3, sourceid %4. Tuner "
-                                  "is locked by an external application.")
-                          .arg(nextRecording->GetTitle())
-                          .arg(nextRecording->GetChanID())
-                          .arg(nextRecording->GetCardID())
-                          .arg(nextRecording->GetSourceID());
-                    QByteArray amsg = msg.toLocal8Bit();
-                    VERBOSE(VB_GENERAL, msg.constData());
+    //VERBOSE(VB_GENERAL, secsleft << " seconds until "
+    //<< ri.GetTitle());
 
-                    QMutexLocker lockit(reclist_lock);
-                    nextRecording->SetRecordingStatus(rsTunerBusy);
-                    nextRecording->AddHistory(true);
-                    statuschanged = true;
-                    continue;
-                }
+    if (secsleft > 30)
+        return false;
 
-                if (!IsBusyRecording(nextRecording))
-                {
-                    // Will use pre-roll settings only if no other
-                    // program is currently being recorded
-                    secsleft -= prerollseconds;
-                }
+    if (nexttv->IsWaking())
+    {
+        if (secsleft > 0)
+        {
+            VERBOSE(VB_SCHEDULE,
+                    QString("WARNING: Slave Backend %1 has NOT come "
+                            "back from sleep yet.  Recording can "
+                            "not begin yet for: %2")
+                    .arg(nexttv->GetHostName())
+                    .arg(ri.GetTitle()));
+        }
+        else if (nexttv->GetLastWakeTime().secsTo(curtime) > 300)
+        {
+            VERBOSE(VB_SCHEDULE,
+                    QString("WARNING: Slave Backend %1 has NOT come "
+                            "back from sleep yet. Setting slave "
+                            "status to unknown and attempting "
+                            "to reschedule around its tuners.")
+                    .arg(nexttv->GetHostName()));
 
-                //VERBOSE(VB_GENERAL, secsleft << " seconds until " << nextRecording->GetTitle());
+            QMap<int, EncoderLink *>::Iterator enciter =
+                m_tvList->begin();
+            for (; enciter != m_tvList->end(); ++enciter)
+            {
+                EncoderLink *enc = *enciter;
+                if (enc->GetHostName() == nexttv->GetHostName())
+                    enc->SetSleepStatus(sStatus_Undefined);
+            }
 
-                if (secsleft > 30)
-                    continue;
+            Reschedule(0);
+        }
 
-                if (nexttv->IsWaking())
-                {
-                    if (secsleft > 0)
-                    {
-                        VERBOSE(VB_SCHEDULE,
-                                QString("WARNING: Slave Backend %1 has NOT come "
-                                        "back from sleep yet.  Recording can "
-                                        "not begin yet for: %2")
-                                .arg(nexttv->GetHostName())
-                                .arg(nextRecording->GetTitle()));
-                    }
-                    else if (nexttv->GetLastWakeTime().secsTo(curtime) > 300)
-                    {
-                        VERBOSE(VB_SCHEDULE,
-                                QString("WARNING: Slave Backend %1 has NOT come "
-                                        "back from sleep yet. Setting slave "
-                                        "status to unknown and attempting "
-                                        "to reschedule around its tuners.")
-                                .arg(nexttv->GetHostName()));
+        return false;
+    }
 
-                        QMap<int, EncoderLink *>::Iterator enciter =
-                            m_tvList->begin();
-                        for (; enciter != m_tvList->end(); ++enciter)
-                        {
-                            EncoderLink *enc = *enciter;
-                            if (enc->GetHostName() == nexttv->GetHostName())
-                                enc->SetSleepStatus(sStatus_Undefined);
-                        }
+    int fsID = -1;
+    if (ri.GetPathname().isEmpty())
+    {
+        QMutexLocker lockit(reclist_lock);
+        QString recording_dir;
+        fsID = FillRecordingDir(
+            ri.GetTitle(),
+            ri.GetHostname(),
+            ri.GetStorageGroup(),
+            ri.GetRecordingStartTime(),
+            ri.GetRecordingEndTime(),
+            ri.GetCardID(),
+            recording_dir,
+            reclist);
+        ri.SetPathname(recording_dir);
+    }
 
-                        Reschedule(0);
-                    }
+    if (!recPendingList[schedid])
+    {
+        nexttv->RecordPending(&ri, max(secsleft, 0),
+                              hasLaterList.contains(schedid));
+        recPendingList[schedid] = true;
+    }
 
-                    continue;
-                }
+    if (secsleft > 0)
+        return false;
 
-                if (nextRecording->GetPathname().isEmpty())
-                {
-                    QMutexLocker lockit(reclist_lock);
-                    QString recording_dir;
-                    fsID = FillRecordingDir(
-                                            nextRecording->GetTitle(),
-                                            nextRecording->GetHostname(),
-                                            nextRecording->GetStorageGroup(),
-                                            nextRecording->GetRecordingStartTime(),
-                                            nextRecording->GetRecordingEndTime(),
-                                            nextRecording->GetCardID(),
-                                            recording_dir,
-                                            reclist);
-                    nextRecording->SetPathname(recording_dir);
-                }
+    QDateTime recstartts = mythCurrentDateTime().addSecs(30);
+    recstartts.setTime(
+        QTime(recstartts.time().hour(), recstartts.time().minute()));
+    ri.SetRecordingStartTime(recstartts);
 
-                if (!recPendingList[schedid])
-                {
-                    nexttv->RecordPending(nextRecording, max(secsleft, 0),
-                                          hasLaterList.contains(schedid));
-                    recPendingList[schedid] = true;
-                }
+    QMutexLocker lockit(reclist_lock);
 
-                if (secsleft > -2)
-                    continue;
+    QString details = QString("%1: channel %2 on cardid %3, sourceid %4")
+        .arg(ri.toString(ProgramInfo::kTitleSubtitle))
+        .arg(ri.GetChanID())
+        .arg(ri.GetCardID())
+        .arg(ri.GetSourceID());
 
-                QDateTime recstartts = mythCurrentDateTime().addSecs(30);
-                recstartts.setTime
-                    (QTime(recstartts.time().hour(), recstartts.time().minute()));
-                nextRecording->SetRecordingStartTime(recstartts);
+    RecStatusTypes recStatus = rsOffLine;
+    if (schedulingEnabled && nexttv->IsConnected())
+    {
+        if (ri.GetRecordingStatus() == rsWillRecord)
+        {
+            recStatus = nexttv->StartRecording(&ri);
+            ri.AddHistory(false);
 
-                QMutexLocker lockit(reclist_lock);
+            // activate auto expirer
+            if (expirer)
+                expirer->Update(ri.GetCardID(), fsID, true);
+        }
+    }
 
-                details = QString("%1: channel %2 on cardid %3, sourceid %4")
-                          .arg(nextRecording->toString(ProgramInfo::kTitleSubtitle))
-                          .arg(nextRecording->GetChanID())
-                          .arg(nextRecording->GetCardID())
-                          .arg(nextRecording->GetSourceID());
-            } // if (nextRecording->GetRecordingStatus() != rsTuning)
+    HandleRecordingStatusChange(ri, recStatus, details);
+    statuschanged = true;
 
-            if (schedulingEnabled && nexttv->IsConnected())
-            {
-                RecStatusTypes recStatus;
+    return false;
+}
 
-                if (nextRecording->GetRecordingStatus() == rsWillRecord)
-                {
-                    recStatus = nexttv->StartRecording(nextRecording);
-                    nextRecording->AddHistory(false);
-                    if (expirer)
-                    {
-                        // activate auto expirer
-                        expirer->Update(nextRecording->GetCardID(), fsID, true);
-                    }
-                }
-                else
-                    recStatus = nexttv->GetRecordingStatus();
+void Scheduler::HandleRecordingStatusChange(
+    RecordingInfo &ri, RecStatusTypes recStatus, const QString &details)
+{
+    if (ri.GetRecordingStatus() == recStatus)
+        return;
 
-                nextRecording->SetRecordingStatus(recStatus);
-                if (recStatus == rsTuning)
-                    continue;
-            }
-            else
-                nextRecording->SetRecordingStatus(rsOffLine);
-            bool doSchedAfterStart =
-                nextRecording->GetRecordingStatus() != rsRecording ||
-                schedAfterStartMap[nextRecording->GetRecordingRuleID()] ||
-                (nextRecording->GetParentRecordingRuleID() &&
-                 schedAfterStartMap[nextRecording->GetParentRecordingRuleID()]);
-            nextRecording->AddHistory(doSchedAfterStart);
+    ri.SetRecordingStatus(recStatus);
 
-            statuschanged = true;
+    if (rsTuning != recStatus)
+    {
+        bool doSchedAfterStart =
+            ((rsRecording != recStatus) && (rsTuning != recStatus)) ||
+            schedAfterStartMap[ri.GetRecordingRuleID()] ||
+            (ri.GetParentRecordingRuleID() &&
+             schedAfterStartMap[ri.GetParentRecordingRuleID()]);
+        ri.AddHistory(doSchedAfterStart);
+    }
 
-            bool is_rec = (nextRecording->GetRecordingStatus() == rsRecording);
-            msg = is_rec ?
-                  QString("Started recording") :
-                  QString("Canceled recording (%1)")
-                  .arg(toString(nextRecording->GetRecordingStatus(),
-                                nextRecording->GetRecordingRuleType()));
+    QString msg = (rsRecording == recStatus) ?
+        QString("Started recording") :
+        ((rsTuning == recStatus) ?
+         QString("Tuning recording") :
+         QString("Canceled recording (%1)")
+         .arg(toString(ri.GetRecordingStatus(), ri.GetRecordingRuleType())));
 
-            VERBOSE(VB_GENERAL, QString("%1: %2").arg(msg).arg(details));
-            gCoreContext->LogEntry("scheduler", LP_NOTICE, msg, details);
+    VERBOSE(VB_GENERAL, QString("%1: %2").arg(msg).arg(details));
+    gCoreContext->LogEntry("scheduler", LP_NOTICE, msg, details);
 
-            if (is_rec)
-                UpdateNextRecord();
+    if ((rsRecording == recStatus) || (rsTuning == recStatus))
+    {
+        UpdateNextRecord();
+    }
+    else if (rsFailed == recStatus)
+    {
+        MythEvent me(QString("FORCE_DELETE_RECORDING %1 %2")
+                     .arg(ri.GetChanID())
+                     .arg(ri.GetRecordingStartTime(ISODate)));
+        gCoreContext->dispatch(me);
+    }
+}
 
-            if (nextRecording->GetRecordingStatus() == rsFailed)
+void Scheduler::HandleTuning(RecordingInfo &ri, bool &statuschanged)
+{
+    if (rsTuning != ri.GetRecordingStatus())
+        return;
+
+    // Determine current recording status
+    QMap<int, EncoderLink*>::iterator tvit = m_tvList->find(ri.GetCardID());
+    RecStatusTypes recStatus = rsTunerBusy;
+    if (tvit == m_tvList->end())
+    {
+        QString msg = QString("Invalid cardid (%1) for %2")
+            .arg(ri.GetCardID()).arg(ri.GetTitle());
+        VERBOSE(VB_GENERAL, LOC + msg);
+    }
+    else
+    {
+        recStatus = (*tvit)->GetRecordingStatus();
+        if (rsTuning == recStatus)
+        {
+            // If tuning is still taking place this long after we
+            // started give up on it so the scheduler can try to
+            // find another broadcast of the same material.
+            QDateTime curtime = QDateTime::currentDateTime();
+            if ((ri.GetRecordingStartTime().secsTo(curtime) > 180) &&
+                (ri.GetScheduledStartTime().secsTo(curtime) > 180))
             {
-                MythEvent me(QString("FORCE_DELETE_RECORDING %1 %2")
-                             .arg(nextRecording->GetChanID())
-                             .arg(nextRecording->GetRecordingStartTime(ISODate)));
-                gCoreContext->dispatch(me);
+                recStatus = rsFailed;
             }
         }
+    }
 
-        if (statuschanged)
+    // If the status has changed, handle it
+    if (rsTuning != recStatus)
+    {
+        QMutexLocker lockit(reclist_lock);
+
+        QString details = QString("%1: channel %2 on cardid %3, sourceid %4")
+            .arg(ri.toString(ProgramInfo::kTitleSubtitle))
+            .arg(ri.GetChanID()).arg(ri.GetCardID()).arg(ri.GetSourceID());
+        HandleRecordingStatusChange(ri, recStatus, details);
+        statuschanged = true;
+    }
+}
+
+void Scheduler::HandleIdleShutdown(
+    bool &blockShutdown, QDateTime &idleSince,
+    int prerollseconds, int idleTimeoutSecs, int idleWaitForRecordingTime)
+{
+    if ((idleTimeoutSecs <= 0) || (m_mainServer == NULL))
+        return;
+
+    // we release the block when a client connects
+    if (blockShutdown)
+        blockShutdown &= !m_mainServer->isClientConnected();
+    else
+    {
+        QDateTime curtime = QDateTime::currentDateTime();
+
+        // find out, if we are currently recording (or LiveTV)
+        bool recording = false;
+        QMap<int, EncoderLink *>::Iterator it;
+        for (it = m_tvList->begin(); (it != m_tvList->end()) &&
+                 !recording; ++it)
         {
-            MythEvent me("SCHEDULE_CHANGE");
-            gCoreContext->dispatch(me);
-            idleSince = QDateTime();
+            if ((*it)->IsBusy())
+                recording = true;
         }
 
-        // if idletimeout is 0, the user disabled the auto-shutdown feature
-        if ((idleTimeoutSecs > 0) && (m_mainServer != NULL))
+        if (!(m_mainServer->isClientConnected()) && !recording)
         {
-            // we release the block when a client connects
-            if (blockShutdown)
-                blockShutdown &= !m_mainServer->isClientConnected();
-            else
+            // have we received a RESET_IDLETIME message?
+            resetIdleTime_lock.lock();
+            if (resetIdleTime)
             {
-                // find out, if we are currently recording (or LiveTV)
-                bool recording = false;
-                QMap<int, EncoderLink *>::Iterator it;
-                for (it = m_tvList->begin(); (it != m_tvList->end()) &&
-                          !recording; ++it)
-                {
-                    if ((*it)->IsBusy())
-                        recording = true;
-                }
+                // yes - so reset the idleSince time
+                idleSince = QDateTime();
+                resetIdleTime = false;
+            }
+            resetIdleTime_lock.unlock();
 
-                if (!(m_mainServer->isClientConnected()) && !recording)
+            if (!idleSince.isValid())
+            {
+                RecIter idleIter = reclist.begin();
+                for ( ; idleIter != reclist.end(); ++idleIter)
+                    if ((*idleIter)->GetRecordingStatus() ==
+                        rsWillRecord)
+                        break;
+
+                if (idleIter != reclist.end())
                 {
-                    // have we received a RESET_IDLETIME message?
-                    resetIdleTime_lock.lock();
-                    if (resetIdleTime)
+                    if (curtime.secsTo
+                        ((*idleIter)->GetRecordingStartTime()) -
+                        prerollseconds >
+                        (idleWaitForRecordingTime * 60) +
+                        idleTimeoutSecs)
                     {
-                        // yes - so reset the idleSince time
-                        idleSince = QDateTime();
-                        resetIdleTime = false;
+                        idleSince = curtime;
                     }
-                    resetIdleTime_lock.unlock();
-
-                    if (!idleSince.isValid())
+                }
+                else
+                    idleSince = curtime;
+            }
+            else
+            {
+                // is the machine already idling the timeout time?
+                if (idleSince.addSecs(idleTimeoutSecs) < curtime)
+                {
+                    // are we waiting for shutdown?
+                    if (m_isShuttingDown)
                     {
-                        RecIter idleIter = reclist.begin();
-                        for ( ; idleIter != reclist.end(); ++idleIter)
-                            if ((*idleIter)->GetRecordingStatus() ==
-                                rsWillRecord)
-                                break;
-
-                        if (idleIter != reclist.end())
+                        // if we have been waiting more that 60secs then assume
+                        // something went wrong so reset and try again
+                        if (idleSince.addSecs(idleTimeoutSecs + 60) <
+                            curtime)
                         {
-                            if (curtime.secsTo
-                                ((*idleIter)->GetRecordingStartTime()) -
-                                prerollseconds >
-                                (idleWaitForRecordingTime * 60) +
-                                idleTimeoutSecs)
-                            {
-                                idleSince = curtime;
-                            }
+                            VERBOSE(VB_IMPORTANT, "Waited more than 60"
+                                    " seconds for shutdown to complete"
+                                    " - resetting idle time");
+                            idleSince = QDateTime();
+                            m_isShuttingDown = false;
                         }
-                        else
-                            idleSince = curtime;
                     }
-                    else
+                    else if (!m_isShuttingDown &&
+                             CheckShutdownServer(prerollseconds,
+                                                 idleSince,
+                                                 blockShutdown))
                     {
-                        // is the machine already idling the timeout time?
-                        if (idleSince.addSecs(idleTimeoutSecs) < curtime)
-                        {
-                            // are we waiting for shutdown?
-                            if (m_isShuttingDown)
-                            {
-                                // if we have been waiting more that 60secs then assume
-                                // something went wrong so reset and try again
-                                if (idleSince.addSecs(idleTimeoutSecs + 60) <
-                                    curtime)
-                                {
-                                    VERBOSE(VB_IMPORTANT, "Waited more than 60"
-                                            " seconds for shutdown to complete"
-                                            " - resetting idle time");
-                                    idleSince = QDateTime();
-                                    m_isShuttingDown = false;
-                                }
-                            }
-                            else if (!m_isShuttingDown &&
-                                     CheckShutdownServer(prerollseconds,
-                                                         idleSince,
-                                                         blockShutdown))
-                            {
-                                ShutdownServer(prerollseconds, idleSince);
-                            }
-                        }
-                        else
-                        {
-                            int itime = idleSince.secsTo(curtime);
-                            QString msg;
-                            if (itime == 1)
-                            {
-                                msg = QString("I\'m idle now... shutdown will "
-                                              "occur in %1 seconds.")
-                                      .arg(idleTimeoutSecs);
-                                VERBOSE(VB_IMPORTANT, msg);
-                                MythEvent me(QString("SHUTDOWN_COUNTDOWN %1")
-                                             .arg(idleTimeoutSecs));
-                                gCoreContext->dispatch(me);
-                            }
-                            else if (itime % 10 == 0)
-                            {
-                                msg = QString("%1 secs left to system "
-                                              "shutdown!")
-                                      .arg(idleTimeoutSecs - itime);
-                                VERBOSE(VB_IDLE, msg);
-                                MythEvent me(QString("SHUTDOWN_COUNTDOWN %1")
-                                             .arg(idleTimeoutSecs - itime));
-                                gCoreContext->dispatch(me);
-                            }
-                        }
+                        ShutdownServer(prerollseconds, idleSince);
                     }
                 }
                 else
                 {
-                    // not idle, make the time invalid
-                    if (idleSince.isValid())
+                    int itime = idleSince.secsTo(curtime);
+                    QString msg;
+                    if (itime == 1)
                     {
-                        MythEvent me(QString("SHUTDOWN_COUNTDOWN -1"));
+                        msg = QString("I\'m idle now... shutdown will "
+                                      "occur in %1 seconds.")
+                            .arg(idleTimeoutSecs);
+                        VERBOSE(VB_IMPORTANT, msg);
+                        MythEvent me(QString("SHUTDOWN_COUNTDOWN %1")
+                                     .arg(idleTimeoutSecs));
                         gCoreContext->dispatch(me);
                     }
-                    idleSince = QDateTime();
+                    else if (itime % 10 == 0)
+                    {
+                        msg = QString("%1 secs left to system "
+                                      "shutdown!")
+                            .arg(idleTimeoutSecs - itime);
+                        VERBOSE(VB_IDLE, msg);
+                        MythEvent me(QString("SHUTDOWN_COUNTDOWN %1")
+                                     .arg(idleTimeoutSecs - itime));
+                        gCoreContext->dispatch(me);
+                    }
                 }
             }
         }
+        else
+        {
+            // not idle, make the time invalid
+            if (idleSince.isValid())
+            {
+                MythEvent me(QString("SHUTDOWN_COUNTDOWN -1"));
+                gCoreContext->dispatch(me);
+            }
+            idleSince = QDateTime();
+        }
     }
 }
 
+
 //returns true, if the shutdown is not blocked
 bool Scheduler::CheckShutdownServer(int prerollseconds, QDateTime &idleSince,
                                     bool &blockShutdown)
@@ -2446,6 +2636,7 @@
         RecordingInfo *pginfo = *recIter;
 
         if ((pginfo->GetRecordingStatus() != rsRecording) &&
+            (pginfo->GetRecordingStatus() != rsTuning) &&
             (pginfo->GetRecordingStatus() != rsWillRecord))
             continue;