From 406927325e6c879f157bc10c63a18be82acf6e9c Mon Sep 17 00:00:00 2001
From: Roger Siddons <dizygotheca@ntlworld.com>
Date: Tue, 7 Oct 2014 11:16:54 +0100
Subject: [PATCH 6/6] Watchlist: Add 'Oldest with Limits' strategy


diff --git a/mythtv/programs/mythfrontend/globalsettings.cpp b/mythtv/programs/mythfrontend/globalsettings.cpp
index 7818a9e..822327d 100644
--- a/mythtv/programs/mythfrontend/globalsettings.cpp
+++ b/mythtv/programs/mythfrontend/globalsettings.cpp
@@ -3262,9 +3262,13 @@ static HostComboBox *PlaybackWLStrategy()
 
     gc->setLabel(PlaybackSettings::tr("Sort method"));
     gc->addSelection(PlaybackSettings::tr("Classic", "Watchlist"), "Classic");
-    gc->setHelpText(PlaybackSettings::tr("The watchlist ordering method. "
-                                         "Classic keeps up-to-date with current "
-                                         "TV."));
+    gc->addSelection(PlaybackSettings::tr(
+                         "Limited Oldest", "Watchlist"), "LimitedOldest");
+    gc->setHelpText(PlaybackSettings::tr(
+                        "The watchlist ordering method. 'Classic' keeps you "
+                        "up-to-date with current TV. 'Limited Oldest' shows "
+                        "oldest->newest whilst highlighting new or popular "
+                        "titles, and relegating ignored ones."));
     return gc;
 }
 
@@ -3303,6 +3307,39 @@ static HostSpinBox *PlaybackWLBlackOut()
     return gs;
 }
 
+static HostSpinBox *PlaybackWLRecentLimit()
+{
+    HostSpinBox *gs = new HostSpinBox("PlaybackWLRecentLimit", 0, 168, 6, true);
+
+    gs->setLabel(WatchListSettings::tr("Hours to keep at top"));
+
+    gs->setValue(6);
+
+    gs->setHelpText(WatchListSettings::tr(
+                        "Titles are promoted to the top if:- "
+                        "they are new titles younger than this interval, "
+                        "or they are watched (on average) within this interval. "
+                        "0 disables this behaviour."));
+    return gs;
+}
+
+static HostSpinBox *PlaybackWLOldLimit()
+{
+    HostSpinBox *gs = new HostSpinBox("PlaybackWLOldLimit", 7, 3650, 7, true);
+
+    gs->setLabel(WatchListSettings::tr("Days before relegating to bottom"));
+
+    gs->setValue(28);
+
+    gs->setHelpText(WatchListSettings::tr(
+                        "Titles are moved to the bottom when they:- "
+                        "are older than this interval, and "
+                        "have not been watched (on average) within this interval. "
+                        "Use a high value to reduce this behaviour."));
+
+    return gs;
+}
+
 WatchListStrategy::WatchListStrategy() :
     TriggeredConfigurationGroup(false, false, true, true)
 {
@@ -3316,6 +3353,13 @@ WatchListStrategy::WatchListStrategy() :
      classic->addChild(PlaybackWLBlackOut());
 
      addTarget("Classic", classic);
+
+     ConfigurationGroup* oldest = new VerticalConfigurationGroup(false,false);
+
+     oldest->addChild(PlaybackWLRecentLimit());
+     oldest->addChild(PlaybackWLOldLimit());
+
+     addTarget("LimitedOldest", oldest);
 };
 
 WatchListSettings::WatchListSettings() :
diff --git a/mythtv/programs/mythfrontend/playbackbox.cpp b/mythtv/programs/mythfrontend/playbackbox.cpp
index eff3d85..a7136d2 100644
--- a/mythtv/programs/mythfrontend/playbackbox.cpp
+++ b/mythtv/programs/mythfrontend/playbackbox.cpp
@@ -434,11 +434,13 @@ PlaybackBox::PlaybackBox(MythScreenStack *parent, QString name,
     int pbOrder        = gCoreContext->GetNumSetting("PlayBoxOrdering", 1);
     // Split out sort order modes, wacky order for backward compatibility
     m_listOrder = (pbOrder >> 1) ^ (m_allOrder = pbOrder & 1);
-    m_watchListStart     = gCoreContext->GetNumSetting("PlaybackWLStart", 0);
+    m_watchListStart      = gCoreContext->GetNumSetting("PlaybackWLStart", 0);
 
-    m_watchListAutoExpire= gCoreContext->GetNumSetting("PlaybackWLAutoExpire", 0);
-    m_watchListMaxAge    = gCoreContext->GetNumSetting("PlaybackWLMaxAge", 60);
-    m_watchListBlackOut  = gCoreContext->GetNumSetting("PlaybackWLBlackOut", 2);
+    m_watchListAutoExpire = gCoreContext->GetNumSetting("PlaybackWLAutoExpire", 0);
+    m_watchListMaxAge     = gCoreContext->GetNumSetting("PlaybackWLMaxAge", 60);
+    m_watchListBlackOut   = gCoreContext->GetNumSetting("PlaybackWLBlackOut", 2);
+    m_watchListRecentLimit= gCoreContext->GetNumSetting("PlaybackWLRecentLimit", 2);
+    m_watchListOldLimit   = gCoreContext->GetNumSetting("PlaybackWLOldLimit", 30);
     m_watchListStrategy   = gCoreContext->GetSetting("WatchListOrder", "Classic");
 
     bool displayCat  = gCoreContext->GetNumSetting("DisplayRecGroupIsCategory", 0);
@@ -1780,6 +1782,94 @@ void PlaybackBox::OrderByClassicStrategy(TitleMap& selections, ProgramOrder& ord
     }
 }
 
+void PlaybackBox::OrderByOldestStrategy(TitleMap& selections, ProgramOrder& ordered)
+{
+    QDateTime now = MythDate::current();
+
+    QMap<int, int> avgDelay;
+    QMap<int, QDateTime> lastDelete;
+
+    MSqlQuery query(MSqlQuery::InitCon());
+    query.prepare("SELECT recordid, avg_delay, last_delete FROM record;");
+
+    if (query.exec())
+    {
+        while (query.next())
+        {
+            int recid = query.value(0).toInt();
+            avgDelay[recid] = query.value(1).toInt();
+            lastDelete[recid] = query.value(2).toDateTime();
+        }
+    }
+
+    TitleMap::iterator it = selections.begin();
+    while (it != selections.end())
+    {
+        ProgramInfo* p = it.value();
+        int recid = p->GetRecordingRuleID();
+        bool knownRule = avgDelay.contains(recid);
+
+        // use priority as primary key in top 32 bits:
+        // 0 = bottom, 1 = middle, 2 = top
+        // use age in secs as a secondary key in low 32 bits to ensure equal
+        // scores are ordered oldest first. Copes with progs up to 136 yrs old
+        score_type score = p->GetScheduledStartTime().secsTo(now);
+
+        // put new recordings or those from rules that are watched quickly, at the top
+        int ageInHours = p->GetScheduledEndTime().secsTo(now) / 3600;
+        if (ageInHours <= m_watchListRecentLimit
+                || (knownRule && avgDelay[recid] <= m_watchListRecentLimit))
+        {
+            score |= 0x0200000000;
+
+            LOG(VB_GUI, LOG_DEBUG,
+                QString("Watchlist: Top:  %1 - '%2' was recorded %3 hrs "
+                        "ago & being watched after %4 hrs")
+                .arg(score, 11)
+                .arg(p->GetTitle())
+                .arg(ageInHours)
+                .arg(avgDelay[recid]));
+        }
+        // recordings go to middle if not yet old enough
+        else if (ageInHours / 24 <= m_watchListOldLimit)
+        {
+            score |= 0x0100000000;
+
+            LOG(VB_GUI, LOG_DEBUG,
+                QString("Watchlist: Middle:%1 - '%2' was recorded %3 hrs ago")
+                .arg(score, 11)
+                .arg(p->GetTitle())
+                .arg(ageInHours));
+        }
+        // or a previous episode for the rule was deleted recently
+        else if (knownRule)
+        {
+            QDateTime deleted = MythDate::as_utc(lastDelete[recid]);
+            if (deleted.isValid()
+                    && deleted.secsTo(now) / 3600 / 24 <= m_watchListOldLimit)
+            {
+                score |= 0x0100000000;
+
+                LOG(VB_GUI, LOG_DEBUG,
+                    QString("Watchlist: Middle:%1 - '%2' was watched %3 days ago")
+                    .arg(score, 11)
+                    .arg(p->GetTitle())
+                    .arg(deleted.secsTo(now) / 3600 / 24));
+            }
+        }
+        else
+            LOG(VB_GUI, LOG_DEBUG,
+                QString("Watchlist: Bottom:%1 - '%2' is %3 hrs old")
+                .arg(score, 11)
+                .arg(p->GetTitle())
+                .arg(ageInHours));
+
+        ordered.insert(score, p);
+
+        ++it;
+    }
+}
+
 bool PlaybackBox::UpdateUILists(void)
 {
     m_isFilling = true;
@@ -1973,7 +2063,7 @@ bool PlaybackBox::UpdateUILists(void)
                     else if (p->IsWatched())
                     {
                         LOG(VB_GUI, LOG_DEBUG,
-                            QString("Watchlist: Watched - %1 %2 (%3x%4)")
+                            QString("Watchlist: Watched  - %1 %2 (%3x%4)")
                             .arg(MythDate::toString(p->GetScheduledStartTime(),
                                                     Qt::ISODate))
                             .arg(p->GetTitle())
@@ -2010,7 +2100,7 @@ bool PlaybackBox::UpdateUILists(void)
                                 watchEpisode[title] = p;
 
                                 LOG(VB_GUI, LOG_DEBUG,
-                                    QString("Watchlist: Replace - %1 %2 (%3x%4) "
+                                    QString("Watchlist: Replace  - %1 %2 (%3x%4) "
                                             "succeeding %5 (%6x%7)")
                                     .arg(MythDate::toString(p->GetScheduledStartTime(),
                                                             Qt::ISODate))
@@ -2118,6 +2208,10 @@ bool PlaybackBox::UpdateUILists(void)
 
             OrderByClassicStrategy(watchEpisode, watchList);
 
+        else if (m_watchListStrategy == "LimitedOldest")
+
+            OrderByOldestStrategy(watchEpisode, watchList);
+
         // populate watchlist group;
         // duplicate keys will appear in reverse alphabetic order
         foreach (ProgramInfo* wp, watchList)
diff --git a/mythtv/programs/mythfrontend/playbackbox.h b/mythtv/programs/mythfrontend/playbackbox.h
index 1266509..b1b5844 100644
--- a/mythtv/programs/mythfrontend/playbackbox.h
+++ b/mythtv/programs/mythfrontend/playbackbox.h
@@ -267,6 +267,7 @@ class PlaybackBox : public ScheduleCommon
     typedef QMap<QString, ProgramInfo*> TitleMap; // progs keyed by title
 
     void OrderByClassicStrategy(TitleMap& selections, ProgramOrder &ordered);
+    void OrderByOldestStrategy(TitleMap& selections, ProgramOrder &ordered);
 
     bool UpdateUILists(void);
     void UpdateUIGroupList(const QStringList &groupPreferences);
@@ -369,6 +370,10 @@ class PlaybackBox : public ScheduleCommon
     int                 m_watchListMaxAge;
     /// adjust exclusion of a title from the Watch List after a delete
     int                 m_watchListBlackOut;
+    /// recordings younger than this (hrs) go to the top
+    int                 m_watchListRecentLimit;
+    /// recordings older than this got to the bottom
+    int                 m_watchListOldLimit;
     /// strategy for ordering the watchlist
     QString             m_watchListStrategy;
     /// allOrder controls the ordering of the "All Programs" list
-- 
1.9.1

