Ticket #12809: 0002-Watchlist-with-lastPlayPos.patch
| File 0002-Watchlist-with-lastPlayPos.patch, 56.1 KB (added by , 9 years ago) |
|---|
-
mythtv/libs/libmyth/programtypes.h
From 5783964c24270d61ca8a71baefbabf2548704a1a Mon Sep 17 00:00:00 2001 From: Roger Siddons <rsiddons@mythtv.org> Date: Thu, 21 Jan 2016 13:46:11 +0000 Subject: [PATCH 2/3] Watchlist with lastPlayPos Watchlist: Refactor & remove debug info The watchlist abuses ProgramInfo::recpriority2 to cache scores for display on the Info/ProgDetails screen. The score/state is of no interest to the user, who cannot alter it. It is debug information that belongs in the logs and has been removed from the screens. Themes should remove WATCH_LIST_SCORE & WATCH_LIST_STATUS widgets from htmls/progdetails*.html. However they are set as empty to prevent breakage for now. Refs #12296 Watchlist: Group by title rather than record rule Watchlist groups titles using the record rule id. Thus different titles recorded by a one rule are confusingly grouped together. And the same title recorded by different rules are shown in different groups. Also a pre-requisite for selection by episode number. Grouping ignores title case, whitespace and punctuation. Ref #12296 Watchlist: Use season/episode when selecting an episode Watchlist always selects the earliest/oldest episode, so the wrong recording is shown when episodes are recorded out of order. This patch uses season/episode data to override the oldest selection, where appropriate. Refs #12296 Watchlist: Add theme widget to show watchlist episode count A new UI widget "watchtotal" enables the watchlist to show the number of episodes available for each title. For example a "recordings" buttonlist specification of: <textarea name="title"> <template>%title%% (|watchtotal| episodes)%% - "|subtitle|"%</template> </textarea> will produce: The Simpsons (15 episodes) - "Homer does something stupid" This widget is only set when the Watchlist group is selected in the groups list - for other groups it will show the usual: The Simpsons - "Homer does something stupid" Refs #12296 Watchlist: Add 'Limited Oldest' strategy Adds a new simplified sort strategy "LimitedOldest" that is more stable, intuitive & useful than the current 'Classic' one. Titles are ordered by record date (oldest first) so the list is predictable and does not spontaneously shuffle. Optionally new titles & quickly-watched titles can be promoted to the top and titles that remain unwatched for a long period can be relegated to the bottom. Two new settings "PlaybackWLRecentLimit" & "PlaybackWLOldLimit" are introduced so the user can adjust this behaviour. User can choose between this and the 'classic' sort strategy via a new setting "PlaybackWLOrder" Fixes #12296 Watchlist: Update watchlist after metadata edits Prevent metadata edits updating the recording list directly. They must be processed by a ProgInfo update in order to update watchlist. Watchlist: Place part-watched shows at top diff --git a/mythtv/libs/libmyth/programtypes.h b/mythtv/libs/libmyth/programtypes.h index bcbb886..0e28ffb 100644
a b typedef enum AvailableStatusTypes { 222 222 } AvailableStatusType; // note stored in uint8_t in ProgramInfo 223 223 MPUBLIC QString toString(AvailableStatusType); 224 224 225 enum WatchListStatus {226 wlDeleted = -4,227 wlEarlier = -3,228 wlWatched = -2,229 wlExpireOff = -1230 };231 232 225 typedef enum AutoExpireTypes { 233 226 kDisableAutoExpire = 0, 234 227 kNormalAutoExpire = 1, -
mythtv/programs/mythfrontend/globalsettings.cpp
diff --git a/mythtv/programs/mythfrontend/globalsettings.cpp b/mythtv/programs/mythfrontend/globalsettings.cpp index 4a8a5b3..aa06722 100644
a b static HostCheckBox *PlaybackWatchList() 3215 3215 gc->setValue(true); 3216 3216 3217 3217 gc->setHelpText(WatchListSettings::tr("The 'Watch List' is an abbreviated " 3218 "list of recordings sorted to "3218 "list of recordings filtered to " 3219 3219 "highlight series and shows that " 3220 3220 "need attention in order to keep up " 3221 3221 "to date.")); … … static HostCheckBox *PlaybackWLAutoExpire() 3254 3254 return gc; 3255 3255 } 3256 3256 3257 static HostComboBox *PlaybackWLStrategy() 3258 { 3259 HostComboBox *gc = new HostComboBox("PlaybackWLOrder"); 3260 3261 gc->setLabel(WatchListSettings::tr("Sort method")); 3262 gc->addSelection(WatchListSettings::tr("Classic", "Watchlist"), "Classic"); 3263 gc->addSelection(WatchListSettings::tr( 3264 "Limited Oldest", "Watchlist"), "LimitedOldest"); 3265 gc->setHelpText(WatchListSettings::tr("The watchlist ordering method. " 3266 "'Classic' learns your viewing habits. " 3267 "'Limited Oldest' simply lists shows by age, but " 3268 "promotes new/popular titles and demotes old ones.")); 3269 return gc; 3270 } 3271 3257 3272 static HostSpinBox *PlaybackWLMaxAge() 3258 3273 { 3259 3274 HostSpinBox *gs = new HostSpinBox("PlaybackWLMaxAge", 30, 180, 10); … … static HostSpinBox *PlaybackWLBlackOut() 3289 3304 return gs; 3290 3305 } 3291 3306 3292 WatchListSettings::WatchListSettings() : 3307 static HostSpinBox *PlaybackWLRecentLimit() 3308 { 3309 HostSpinBox *gs = new HostSpinBox("PlaybackWLRecentLimit", 0, 168, 6, true); 3310 3311 gs->setLabel(WatchListSettings::tr("Hours to keep at top")); 3312 3313 gs->setValue(6); 3314 3315 gs->setHelpText(WatchListSettings::tr( 3316 "Titles are promoted to the top when they are younger " 3317 "than, or previous episodes have been watched " 3318 "(on average) within, this interval. " 3319 "0 disables this behaviour.")); 3320 return gs; 3321 } 3322 3323 static HostSpinBox *PlaybackWLOldLimit() 3324 { 3325 HostSpinBox *gs = new HostSpinBox("PlaybackWLOldLimit", 7, 3650, 7, true); 3326 3327 gs->setLabel(WatchListSettings::tr("Days before relegating to bottom")); 3328 3329 gs->setValue(28); 3330 3331 gs->setHelpText(WatchListSettings::tr( 3332 "Titles are relegated to the bottom when they " 3333 "are older than, and previous episodes have not " 3334 "been watched (on average), within this interval. " 3335 "Use a high value to prevent this behaviour.")); 3336 3337 return gs; 3338 } 3339 3340 WatchListStrategy::WatchListStrategy() : 3293 3341 TriggeredConfigurationGroup(false, false, true, true) 3294 3342 { 3343 Setting* strategy = PlaybackWLStrategy(); 3344 addChild(strategy); 3345 setTrigger(strategy); 3346 3347 ConfigurationGroup* classic = new VerticalConfigurationGroup(false,false); 3348 3349 classic->addChild(PlaybackWLMaxAge()); 3350 classic->addChild(PlaybackWLBlackOut()); 3295 3351 3352 addTarget("Classic", classic); 3353 3354 ConfigurationGroup* oldest = new VerticalConfigurationGroup(false,false); 3355 3356 oldest->addChild(PlaybackWLRecentLimit()); 3357 oldest->addChild(PlaybackWLOldLimit()); 3358 3359 addTarget("LimitedOldest", oldest); 3360 } 3361 3362 WatchListSettings::WatchListSettings() : 3363 TriggeredConfigurationGroup(false, false, true, true) 3364 { 3296 3365 Setting* watchList = PlaybackWatchList(); 3297 3366 addChild(watchList); 3298 3367 setTrigger(watchList); 3299 3368 3300 ConfigurationGroup* settings = new VerticalConfigurationGroup(false); 3369 ConfigurationGroup* settings = 3370 new VerticalConfigurationGroup(false,false,true,true); 3301 3371 3302 3372 settings->addChild(PlaybackWLStart()); 3303 3373 settings->addChild(PlaybackWLAutoExpire()); 3304 settings->addChild(PlaybackWLMaxAge()); 3305 settings->addChild(PlaybackWLBlackOut()); 3374 settings->addChild(new WatchListStrategy()); 3306 3375 3307 3376 addTarget("1", settings); 3308 3309 addTarget("0", new VerticalConfigurationGroup(true)); 3310 }; 3377 addTarget("0", new VerticalConfigurationGroup(false, false)); 3378 } 3311 3379 3312 3380 static HostCheckBox *LCDShowTime() 3313 3381 { -
mythtv/programs/mythfrontend/globalsettings.h
diff --git a/mythtv/programs/mythfrontend/globalsettings.h b/mythtv/programs/mythfrontend/globalsettings.h index 2de0197..fc114cd 100644
a b class LcdSettings : public TriggeredConfigurationGroup 38 38 LcdSettings(); 39 39 }; 40 40 41 class WatchListStrategy : public TriggeredConfigurationGroup 42 { 43 Q_OBJECT 44 45 public: 46 WatchListStrategy(); 47 }; 41 48 42 49 class WatchListSettings : public TriggeredConfigurationGroup 43 50 { -
mythtv/programs/mythfrontend/playbackbox.cpp
diff --git a/mythtv/programs/mythfrontend/playbackbox.cpp b/mythtv/programs/mythfrontend/playbackbox.cpp index fe71685..76d8ac8 100644
a b static int comp_originalAirDate_rev(const ProgramInfo *a, const ProgramInfo *b) 101 101 return (dt1 > dt2 ? 1 : -1); 102 102 } 103 103 104 static int comp_recpriority2(const ProgramInfo *a, const ProgramInfo *b)105 {106 if (a->GetRecordingPriority2() == b->GetRecordingPriority2())107 return (a->GetRecordingStartTime() <108 b->GetRecordingStartTime() ? 1 : -1);109 else110 return (a->GetRecordingPriority2() <111 b->GetRecordingPriority2() ? 1 : -1);112 }113 114 104 static int comp_recordDate(const ProgramInfo *a, const ProgramInfo *b) 115 105 { 116 106 if (a->GetScheduledStartTime().date() == b->GetScheduledStartTime().date()) … … static bool comp_originalAirDate_rev_less_than( 179 169 return comp_originalAirDate_rev(a, b) < 0; 180 170 } 181 171 182 static bool comp_recpriority2_less_than(183 const ProgramInfo *a, const ProgramInfo *b)184 {185 return comp_recpriority2(a, b) < 0;186 }187 188 172 static bool comp_recordDate_less_than( 189 173 const ProgramInfo *a, const ProgramInfo *b) 190 174 { … … PlaybackBox::PlaybackBox(MythScreenStack *parent, QString name, 405 389 m_titleView(false), 406 390 m_useCategories(false), 407 391 m_useRecGroups(false), 408 m_watchListAutoExpire(false),409 m_watchListMaxAge(60), m_watchListBlackOut(2),410 392 m_listOrder(1), 411 393 // Recording Group settings 412 394 m_groupDisplayName(ProgramInfo::i18n("All Programs")), … … PlaybackBox::PlaybackBox(MythScreenStack *parent, QString name, 449 431 int pbOrder = gCoreContext->GetNumSetting("PlayBoxOrdering", 1); 450 432 // Split out sort order modes, wacky order for backward compatibility 451 433 m_listOrder = (pbOrder >> 1) ^ (m_allOrder = pbOrder & 1); 452 m_watchListStart = gCoreContext->GetNumSetting("PlaybackWLStart", 0); 453 454 m_watchListAutoExpire= gCoreContext->GetNumSetting("PlaybackWLAutoExpire", 0); 455 m_watchListMaxAge = gCoreContext->GetNumSetting("PlaybackWLMaxAge", 60); 456 m_watchListBlackOut = gCoreContext->GetNumSetting("PlaybackWLBlackOut", 2); 434 m_watchListStart = gCoreContext->GetNumSetting("PlaybackWLStart", 0); 457 435 458 436 bool displayCat = gCoreContext->GetNumSetting("DisplayRecGroupIsCategory", 0); 459 437 … … void PlaybackBox::ItemLoaded(MythUIButtonListItem *item) 956 934 item->SetFontState(state); 957 935 958 936 InfoMap infoMap; 937 938 // watchlist episode count only set when watchlist group is selected 939 if (groupname == m_watchGroupLabel) 940 infoMap["watchtotal"] = m_watchlist.GetTotal(*pginfo); 941 959 942 pginfo->ToMap(infoMap); 960 943 item->SetTextFromMap(infoMap); 961 944 … … bool PlaybackBox::UpdateUILists(void) 1629 1612 m_progsInDB = 0; 1630 1613 m_titleList.clear(); 1631 1614 m_progLists.clear(); 1615 m_watchlist.Clear(); 1632 1616 m_recordingList->Reset(); 1633 1617 m_groupList->Reset(); 1634 1618 if (m_recgroupList) … … bool PlaybackBox::UpdateUILists(void) 1643 1627 1644 1628 QMap<QString, QString> sortedList; 1645 1629 QMap<int, QString> searchRule; 1646 QMap<int, int> recidEpisodes;1647 1630 1648 1631 m_programInfoCache.Refresh(); 1649 1632 … … bool PlaybackBox::UpdateUILists(void) 1793 1776 if ((m_viewMask & VIEW_WATCHLIST) && 1794 1777 pRecgroup != "LiveTV" && pRecgroup != "Deleted") 1795 1778 { 1796 if (m_watchListAutoExpire && !p->IsAutoExpirable()) 1797 { 1798 p->SetRecordingPriority2(wlExpireOff); 1799 LOG(VB_FILE, LOG_INFO, QString("Auto-expire off: %1") 1800 .arg(p->GetTitle())); 1801 } 1802 else if (p->IsWatched()) 1803 { 1804 p->SetRecordingPriority2(wlWatched); 1805 LOG(VB_FILE, LOG_INFO, 1806 QString("Marked as 'watched': %1") 1807 .arg(p->GetTitle())); 1808 } 1809 else 1810 { 1811 if (p->GetRecordingRuleID()) 1812 recidEpisodes[p->GetRecordingRuleID()] += 1; 1813 if (recidEpisodes[p->GetRecordingRuleID()] == 1 || 1814 !p->GetRecordingRuleID()) 1815 { 1816 m_progLists[m_watchGroupLabel].push_front(p); 1817 m_progLists[m_watchGroupLabel].setAutoDelete(false); 1818 } 1819 else 1820 { 1821 p->SetRecordingPriority2(wlEarlier); 1822 LOG(VB_FILE, LOG_INFO, 1823 QString("Not the earliest: %1") 1824 .arg(p->GetTitle())); 1825 } 1826 } 1779 m_watchlist.Add(p); 1827 1780 } 1828 1781 } 1829 1782 } … … bool PlaybackBox::UpdateUILists(void) 1898 1851 } 1899 1852 } 1900 1853 1901 if (!m_ progLists[m_watchGroupLabel].empty())1854 if (!m_watchlist.Empty()) 1902 1855 { 1903 QDateTime now = MythDate::current(); 1904 int baseValue = m_watchListMaxAge * 2 / 3; 1905 1906 QMap<int, int> recType; 1907 QMap<int, int> maxEpisodes; 1908 QMap<int, int> avgDelay; 1909 QMap<int, int> spanHours; 1910 QMap<int, int> delHours; 1911 QMap<int, int> nextHours; 1912 1913 MSqlQuery query(MSqlQuery::InitCon()); 1914 query.prepare("SELECT recordid, type, maxepisodes, avg_delay, " 1915 "next_record, last_record, last_delete FROM record;"); 1916 1917 if (query.exec()) 1918 { 1919 while (query.next()) 1920 { 1921 int recid = query.value(0).toInt(); 1922 recType[recid] = query.value(1).toInt(); 1923 maxEpisodes[recid] = query.value(2).toInt(); 1924 avgDelay[recid] = query.value(3).toInt(); 1925 1926 QDateTime next_record = 1927 MythDate::as_utc(query.value(4).toDateTime()); 1928 QDateTime last_record = 1929 MythDate::as_utc(query.value(5).toDateTime()); 1930 QDateTime last_delete = 1931 MythDate::as_utc(query.value(6).toDateTime()); 1932 1933 // Time between the last and next recordings 1934 spanHours[recid] = 1000; 1935 if (last_record.isValid() && next_record.isValid()) 1936 spanHours[recid] = 1937 last_record.secsTo(next_record) / 3600 + 1; 1938 1939 // Time since the last episode was deleted 1940 delHours[recid] = 1000; 1941 if (last_delete.isValid()) 1942 delHours[recid] = last_delete.secsTo(now) / 3600 + 1; 1943 1944 // Time until the next recording if any 1945 if (next_record.isValid()) 1946 nextHours[recid] = now.secsTo(next_record) / 3600 + 1; 1947 } 1948 } 1949 1950 ProgramList::iterator pit = m_progLists[m_watchGroupLabel].begin(); 1951 while (pit != m_progLists[m_watchGroupLabel].end()) 1952 { 1953 int recid = (*pit)->GetRecordingRuleID(); 1954 int avgd = avgDelay[recid]; 1955 1956 if (avgd == 0) 1957 avgd = 100; 1958 1959 // Set the intervals beyond range if there is no record entry 1960 if (spanHours[recid] == 0) 1961 { 1962 spanHours[recid] = 1000; 1963 delHours[recid] = 1000; 1964 } 1965 1966 // add point equal to baseValue for each additional episode 1967 if (!(*pit)->GetRecordingRuleID() || maxEpisodes[recid] > 0) 1968 (*pit)->SetRecordingPriority2(0); 1969 else 1970 { 1971 (*pit)->SetRecordingPriority2( 1972 (recidEpisodes[(*pit)->GetRecordingRuleID()] - 1) * 1973 baseValue); 1974 } 1975 1976 // add points every 3hr leading up to the next recording 1977 if (nextHours[recid] > 0 && nextHours[recid] < baseValue * 3) 1978 { 1979 (*pit)->SetRecordingPriority2( 1980 (*pit)->GetRecordingPriority2() + 1981 (baseValue * 3 - nextHours[recid]) / 3); 1982 } 1983 1984 int hrs = (*pit)->GetScheduledEndTime().secsTo(now) / 3600; 1985 if (hrs < 1) 1986 hrs = 1; 1987 1988 // add points for a new recording that decrease each hour 1989 if (hrs < 42) 1990 { 1991 (*pit)->SetRecordingPriority2( 1992 (*pit)->GetRecordingPriority2() + 42 - hrs); 1993 } 1994 1995 // add points for how close the recorded time of day is to 'now' 1996 (*pit)->SetRecordingPriority2( 1997 (*pit)->GetRecordingPriority2() + abs((hrs % 24) - 12) * 2); 1998 1999 // Daily 2000 if (spanHours[recid] < 50 || 2001 recType[recid] == kDailyRecord) 2002 { 2003 if (delHours[recid] < m_watchListBlackOut * 4) 2004 { 2005 (*pit)->SetRecordingPriority2(wlDeleted); 2006 LOG(VB_FILE, LOG_INFO, 2007 QString("Recently deleted daily: %1") 2008 .arg((*pit)->GetTitle())); 2009 pit = m_progLists[m_watchGroupLabel].erase(pit); 2010 continue; 2011 } 2012 else 2013 { 2014 LOG(VB_FILE, LOG_INFO, QString("Daily interval: %1") 2015 .arg((*pit)->GetTitle())); 2016 2017 if (maxEpisodes[recid] > 0) 2018 { 2019 (*pit)->SetRecordingPriority2( 2020 (*pit)->GetRecordingPriority2() + 2021 (baseValue / 2) + (hrs / 24)); 2022 } 2023 else 2024 { 2025 (*pit)->SetRecordingPriority2( 2026 (*pit)->GetRecordingPriority2() + 2027 (baseValue / 5) + hrs); 2028 } 2029 } 2030 } 2031 // Weekly 2032 else if (nextHours[recid] || 2033 recType[recid] == kWeeklyRecord) 2034 2035 { 2036 if (delHours[recid] < (m_watchListBlackOut * 24) - 4) 2037 { 2038 (*pit)->SetRecordingPriority2(wlDeleted); 2039 LOG(VB_FILE, LOG_INFO, 2040 QString("Recently deleted weekly: %1") 2041 .arg((*pit)->GetTitle())); 2042 pit = m_progLists[m_watchGroupLabel].erase(pit); 2043 continue; 2044 } 2045 else 2046 { 2047 LOG(VB_FILE, LOG_INFO, QString("Weekly interval: %1") 2048 .arg((*pit)->GetTitle())); 2049 2050 if (maxEpisodes[recid] > 0) 2051 { 2052 (*pit)->SetRecordingPriority2( 2053 (*pit)->GetRecordingPriority2() + 2054 (baseValue / 2) + (hrs / 24)); 2055 } 2056 else 2057 { 2058 (*pit)->SetRecordingPriority2( 2059 (*pit)->GetRecordingPriority2() + 2060 (baseValue / 3) + (baseValue * hrs / 24 / 4)); 2061 } 2062 } 2063 } 2064 // Not recurring 2065 else 2066 { 2067 if (delHours[recid] < (m_watchListBlackOut * 48) - 4) 2068 { 2069 (*pit)->SetRecordingPriority2(wlDeleted); 2070 pit = m_progLists[m_watchGroupLabel].erase(pit); 2071 continue; 2072 } 2073 else 2074 { 2075 // add points for a new Single or final episode 2076 if (hrs < 36) 2077 { 2078 (*pit)->SetRecordingPriority2( 2079 (*pit)->GetRecordingPriority2() + 2080 baseValue * (36 - hrs) / 36); 2081 } 2082 2083 if (avgd != 100) 2084 { 2085 if (maxEpisodes[recid] > 0) 2086 { 2087 (*pit)->SetRecordingPriority2( 2088 (*pit)->GetRecordingPriority2() + 2089 (baseValue / 2) + (hrs / 24)); 2090 } 2091 else 2092 { 2093 (*pit)->SetRecordingPriority2( 2094 (*pit)->GetRecordingPriority2() + 2095 (baseValue / 3) + (baseValue * hrs / 24 / 4)); 2096 } 2097 } 2098 else if ((hrs / 24) < m_watchListMaxAge) 2099 { 2100 (*pit)->SetRecordingPriority2( 2101 (*pit)->GetRecordingPriority2() + 2102 hrs / 24); 2103 } 2104 else 2105 { 2106 (*pit)->SetRecordingPriority2( 2107 (*pit)->GetRecordingPriority2() + 2108 m_watchListMaxAge); 2109 } 2110 } 2111 } 2112 2113 // Factor based on the average time shift delay. 2114 // Scale the avgd range of 0 thru 200 hours to 133% thru 67% 2115 int delaypct = avgd / 3 + 67; 2116 2117 if (avgd < 100) 2118 { 2119 (*pit)->SetRecordingPriority2( 2120 (*pit)->GetRecordingPriority2() * (200 - delaypct) / 100); 2121 } 2122 else if (avgd > 100) 2123 { 2124 (*pit)->SetRecordingPriority2( 2125 (*pit)->GetRecordingPriority2() * 100 / delaypct); 2126 } 2127 2128 LOG(VB_FILE, LOG_INFO, QString(" %1 %2 %3") 2129 .arg(MythDate::toString((*pit)->GetScheduledStartTime(), 2130 MythDate::kDateShort)) 2131 .arg((*pit)->GetRecordingPriority2()) 2132 .arg((*pit)->GetTitle())); 1856 // Shows with same score will appear in reverse alphabetic order 1857 foreach (ProgramInfo* wp, m_watchlist.Order()) 1858 m_progLists[m_watchGroupLabel].push_front(wp); 2133 1859 2134 ++pit; 2135 } 2136 std::stable_sort(m_progLists[m_watchGroupLabel].begin(), 2137 m_progLists[m_watchGroupLabel].end(), 2138 comp_recpriority2_less_than); 1860 m_progLists[m_watchGroupLabel].setAutoDelete(false); 2139 1861 } 2140 1862 2141 1863 m_titleList = QStringList(""); … … void PlaybackBox::HandleRecordingAddEvent(const ProgramInfo &evinfo) 4449 4171 4450 4172 void PlaybackBox::HandleUpdateItemEvent(uint recordingId, uint flags) 4451 4173 { 4452 // Changing recording group full reload 4453 if (flags & PIC_RECGROUP_CHANGED) 4174 // Changing recording group or watchlist-dependent metadata requires full reload 4175 if ((flags & PIC_RECGROUP_CHANGED) || 4176 ((flags & PIC_WATCHLIST_CHANGED) && (m_viewMask & VIEW_WATCHLIST))) 4454 4177 { 4455 4178 ScheduleUpdateUIList(); 4456 4179 } … … void PlaybackBox::saveRecMetadata(const QString &newTitle, 4897 4620 { 4898 4621 m_recordingList->RemoveItem(item); 4899 4622 } 4900 else4901 {4902 QString tempSubTitle = newTitle;4903 if (!newSubtitle.trimmed().isEmpty())4904 tempSubTitle = QString("%1 - \"%2\"")4905 .arg(tempSubTitle).arg(newSubtitle);4906 4907 QString seasone;4908 QString seasonx;4909 QString season;4910 QString episode;4911 if (newSeason > 0 || newEpisode > 0)4912 {4913 season = format_season_and_episode(newSeason, 1);4914 episode = format_season_and_episode(newEpisode, 1);4915 seasone = QString("s%1e%2")4916 .arg(format_season_and_episode(newSeason, 2))4917 .arg(format_season_and_episode(newEpisode, 2));4918 seasonx = QString("%1x%2")4919 .arg(format_season_and_episode(newSeason, 1))4920 .arg(format_season_and_episode(newEpisode, 2));4921 }4922 4923 item->SetText(tempSubTitle, "titlesubtitle");4924 item->SetText(newTitle, "title");4925 item->SetText(newSubtitle, "subtitle");4926 item->SetText(newInetref, "inetref");4927 item->SetText(seasonx, "00x00");4928 item->SetText(seasone, "s00e00");4929 item->SetText(season, "season");4930 item->SetText(episode, "episode");4931 if (newDescription != NULL)4932 item->SetText(newDescription, "description");4933 }4934 4623 4935 4624 pginfo->SaveInetRef(newInetref); 4936 4625 pginfo->SaveSeasonEpisode(newSeason, newEpisode); 4937 4626 4938 4627 RecordingInfo ri(*pginfo); 4939 4628 ri.ApplyRecordRecTitleChange(newTitle, newSubtitle, newDescription); 4940 *pginfo = ri;4941 4629 } 4942 4630 4943 4631 void PlaybackBox::setRecGroup(QString newRecGroup) … … void PlaybackBox::setRecGroup(QString newRecGroup) 4991 4679 4992 4680 RecordingInfo ri(*p); 4993 4681 ri.ApplyRecordRecGroupChange(newRecGroup); 4994 *p = ri;4995 4682 } 4996 4683 doClearPlaylist(); 4997 UpdateUILists();4998 4684 return; 4999 4685 } 5000 4686 … … void PlaybackBox::setRecGroup(QString newRecGroup) 5009 4695 5010 4696 RecordingInfo ri(*p); 5011 4697 ri.ApplyRecordRecGroupChange(newRecGroup); 5012 *p = ri;5013 UpdateUILists();5014 4698 } 5015 4699 5016 4700 void PlaybackBox::setPlayGroup(QString newPlayGroup) … … void PlaybackBox::setPlayGroup(QString newPlayGroup) 5034 4718 { 5035 4719 RecordingInfo ri(*tmpItem); 5036 4720 ri.ApplyRecordPlayGroupChange(newPlayGroup); 5037 *tmpItem = ri;5038 4721 } 5039 4722 } 5040 4723 doClearPlaylist(); … … void PlaybackBox::setPlayGroup(QString newPlayGroup) 5043 4726 { 5044 4727 RecordingInfo ri(*tmpItem); 5045 4728 ri.ApplyRecordPlayGroupChange(newPlayGroup); 5046 *tmpItem = ri;5047 4729 } 5048 4730 } 5049 4731 … … bool PlaybackBox::PbbJobQueue::IsJobQueuedOrRunning(int jobType, uint chanid, 5642 5324 IsJobRunning(jobType, chanid, recstartts); 5643 5325 } 5644 5326 5327 //////////////////////////////////////////////////////// 5328 5329 ProgramInfo* WatchGroup::GetFirst() const 5330 { 5331 // Use earliest episode of the 3 variants 5332 ProgramInfo* earliest = m_season; 5333 5334 if (m_episode && (!earliest || m_episode->GetScheduledStartTime() 5335 < earliest->GetScheduledStartTime())) 5336 earliest = m_episode; 5337 5338 if (m_date && (!earliest || m_date->GetScheduledStartTime() 5339 < earliest->GetScheduledStartTime())) 5340 earliest = m_date; 5341 5342 return earliest; 5343 } 5344 5345 /*! 5346 * \brief Insert episode into watchlist 5347 * \param current Current earliest episode of a variant 5348 * \param p Episode being added 5349 */ 5350 void WatchGroup::Add(ProgramInfo* ¤t, ProgramInfo* p) 5351 { 5352 ++m_count; 5353 5354 uint ps(p->GetSeason()); 5355 uint pe(p->GetEpisode()); 5356 QDateTime pt(p->GetScheduledStartTime()); 5357 5358 if (!current) 5359 { 5360 // first episode with this title 5361 current = p; 5362 5363 LOG(VB_GUI, LOG_DEBUG, 5364 QString("Watchlist: New - %1 %2 (%3x%4)") 5365 .arg(MythDate::toString(pt, Qt::ISODate)) 5366 .arg(p->GetTitle()).arg(ps).arg(pe)); 5367 return; 5368 } 5369 5370 // duplicate title 5371 uint cs(current->GetSeason()); 5372 uint ce(current->GetEpisode()); 5373 QDateTime ct(current->GetScheduledStartTime()); 5374 5375 // succeed earliest if; 5376 // - earlier season, or 5377 // - same season, both with episodes and earlier episode, or 5378 // - same season, at least one episode undefined and earlier recording 5379 if (ps < cs 5380 || (ps == cs 5381 && ((pe > 0 && ce > 0 && pe < ce) 5382 || ((pe == 0 || ce == 0) && pt < ct)))) 5383 { 5384 // replace existing episode 5385 current = p; 5386 5387 LOG(VB_GUI, LOG_DEBUG, 5388 QString("Watchlist: Replace - %1 %2 (%3x%4) " 5389 "succeeding %5 (%6x%7)") 5390 .arg(MythDate::toString(pt, Qt::ISODate)) 5391 .arg(p->GetTitle()) 5392 .arg(ps).arg(pe) 5393 .arg(MythDate::toString(ct, Qt::ISODate)) 5394 .arg(cs).arg(ce)); 5395 } 5396 else 5397 LOG(VB_GUI, LOG_DEBUG, 5398 QString("Watchlist: Earlier - %1 %2 (%3x%4)") 5399 .arg(MythDate::toString(pt, Qt::ISODate)) 5400 .arg(p->GetTitle()) 5401 .arg(ps).arg(pe)); 5402 } 5403 5404 WatchList::WatchList() 5405 : m_list(), 5406 m_watchListAutoExpire(gCoreContext->GetNumSetting("PlaybackWLAutoExpire", 0)), 5407 m_watchListMaxAge(gCoreContext->GetNumSetting("PlaybackWLMaxAge", 60)), 5408 m_watchListBlackOut(gCoreContext->GetNumSetting("PlaybackWLBlackOut", 2)), 5409 m_watchListRecentLimit(gCoreContext->GetNumSetting("PlaybackWLRecentLimit", 2)), 5410 m_watchListOldLimit(gCoreContext->GetNumSetting("PlaybackWLOldLimit", 30)), 5411 m_watchListStrategy(gCoreContext->GetSetting("PlaybackWLOrder", "Classic")) 5412 {} 5413 5414 // Group titles ignore case, punctuation & whitespace 5415 QString WatchList::GroupOf(const ProgramInfo &pginfo) 5416 { 5417 QRegExp chaff("\\W"); 5418 return pginfo.GetTitle().remove(chaff).toLower(); 5419 } 5420 5421 /// Add episode to watchlist 5422 void WatchList::Add(ProgramInfo *p) 5423 { 5424 if (!p) 5425 return; 5426 5427 if (p->IsWatched()) 5428 { 5429 // Ignore watched 5430 LOG(VB_GUI, LOG_DEBUG, 5431 QString("Watchlist: Watched - %1 %2 (%3x%4)") 5432 .arg(MythDate::toString(p->GetScheduledStartTime(), Qt::ISODate)) 5433 .arg(p->GetTitle()) 5434 .arg(p->GetSeason()) 5435 .arg(p->GetEpisode())); 5436 } 5437 else if (m_watchListAutoExpire && !p->IsAutoExpirable()) 5438 { 5439 LOG(VB_GUI, LOG_DEBUG, 5440 QString("Watchlist: No expire - %1 %2 (%3x%4)") 5441 .arg(MythDate::toString(p->GetScheduledStartTime(), 5442 Qt::ISODate)) 5443 .arg(p->GetTitle()) 5444 .arg(p->GetSeason()) 5445 .arg(p->GetEpisode())); 5446 } 5447 else 5448 { 5449 // Create group if it doesn't exist 5450 WatchGroup &group = m_list[GroupOf(*p)]; 5451 5452 // Process according to variant 5453 if (p->GetSeason() > 0) 5454 group.AddSeason(p); 5455 else if (p->GetEpisode() > 0) 5456 group.AddEpisode(p); 5457 else 5458 group.AddDate(p); 5459 } 5460 } 5461 5462 /// Return sorted watchlist 5463 WatchList::ProgramOrder WatchList::Order() 5464 { 5465 if (m_watchListStrategy == "Classic") 5466 return OrderByClassicStrategy(); 5467 5468 if (m_watchListStrategy == "LimitedOldest") 5469 return OrderByOldestStrategy(); 5470 5471 return ProgramOrder(); 5472 } 5473 5474 /// Sort programs by original Watchlist rules 5475 WatchList::ProgramOrder WatchList::OrderByClassicStrategy() 5476 { 5477 QDateTime now = MythDate::current(); 5478 int baseValue = m_watchListMaxAge * 2 / 3; 5479 5480 QMap<int, int> recType; 5481 QMap<int, int> maxEpisodes; 5482 QMap<int, int> avgDelay; 5483 QMap<int, int> spanHours; 5484 QMap<int, int> delHours; 5485 QMap<int, int> nextHours; 5486 5487 MSqlQuery query(MSqlQuery::InitCon()); 5488 query.prepare("SELECT recordid, type, maxepisodes, avg_delay, " 5489 "next_record, last_record, last_delete FROM record;"); 5490 5491 if (query.exec()) 5492 { 5493 while (query.next()) 5494 { 5495 int recid = query.value(0).toInt(); 5496 recType[recid] = query.value(1).toInt(); 5497 maxEpisodes[recid] = query.value(2).toInt(); 5498 avgDelay[recid] = query.value(3).toInt(); 5499 5500 QDateTime next_record = 5501 MythDate::as_utc(query.value(4).toDateTime()); 5502 QDateTime last_record = 5503 MythDate::as_utc(query.value(5).toDateTime()); 5504 QDateTime last_delete = 5505 MythDate::as_utc(query.value(6).toDateTime()); 5506 5507 // Time between the last and next recordings 5508 spanHours[recid] = 1000; 5509 if (last_record.isValid() && next_record.isValid()) 5510 spanHours[recid] = 5511 last_record.secsTo(next_record) / 3600 + 1; 5512 5513 // Time since the last episode was deleted 5514 delHours[recid] = 1000; 5515 if (last_delete.isValid()) 5516 delHours[recid] = last_delete.secsTo(now) / 3600 + 1; 5517 5518 // Time until the next recording if any 5519 if (next_record.isValid()) 5520 nextHours[recid] = now.secsTo(next_record) / 3600 + 1; 5521 } 5522 } 5523 5524 ProgramOrder ordered; 5525 foreach (const WatchGroup &group, m_list) 5526 { 5527 ProgramInfo* p = group.GetFirst(); 5528 uint score = 0; 5529 int recid = p->GetRecordingRuleID(); 5530 int avgd = avgDelay[recid]; 5531 5532 if (avgd == 0) 5533 avgd = 100; 5534 5535 // Set the intervals beyond range if there is no record entry 5536 if (spanHours[recid] == 0) 5537 { 5538 spanHours[recid] = 1000; 5539 delHours[recid] = 1000; 5540 } 5541 5542 // add point equal to baseValue for each additional episode 5543 if (recid && maxEpisodes[recid] == 0) 5544 score += (group.GetCount() - 1) * baseValue; 5545 5546 // add points every 3hr leading up to the next recording 5547 if (nextHours[recid] > 0 && nextHours[recid] < baseValue * 3) 5548 score += (baseValue * 3 - nextHours[recid]) / 3; 5549 5550 int hrs = p->GetScheduledEndTime().secsTo(now) / 3600; 5551 if (hrs < 1) 5552 hrs = 1; 5553 5554 // add points for a new recording that decrease each hour 5555 if (hrs < 42) 5556 score += 42 - hrs; 5557 5558 // add points for how close the recorded time of day is to 'now' 5559 score += abs((hrs % 24) - 12) * 2; 5560 5561 // Daily 5562 if (spanHours[recid] < 50 || 5563 recType[recid] == kDailyRecord) 5564 { 5565 if (delHours[recid] < m_watchListBlackOut * 4) 5566 { 5567 LOG(VB_GUI, LOG_DEBUG, 5568 QString("Watchlist: Recently deleted daily: %1") 5569 .arg(p->GetTitle())); 5570 5571 continue; 5572 } 5573 else 5574 { 5575 LOG(VB_GUI, LOG_DEBUG, QString("Watchlist: Daily interval: %1") 5576 .arg(p->GetTitle())); 5577 5578 if (maxEpisodes[recid] > 0) 5579 score += (baseValue / 2) + (hrs / 24); 5580 else 5581 score += (baseValue / 5) + hrs; 5582 } 5583 } 5584 // Weekly 5585 else if (nextHours[recid] || 5586 recType[recid] == kWeeklyRecord) 5587 5588 { 5589 if (delHours[recid] < (m_watchListBlackOut * 24) - 4) 5590 { 5591 LOG(VB_GUI, LOG_DEBUG, 5592 QString("Watchlist: Recently deleted weekly: %1") 5593 .arg(p->GetTitle())); 5594 5595 continue; 5596 } 5597 else 5598 { 5599 LOG(VB_GUI, LOG_DEBUG, QString("Watchlist: Weekly interval: %1") 5600 .arg(p->GetTitle())); 5601 5602 if (maxEpisodes[recid] > 0) 5603 score += (baseValue / 2) + (hrs / 24); 5604 else 5605 score += (baseValue / 3) + (baseValue * hrs / 24 / 4); 5606 } 5607 } 5608 // Not recurring 5609 else 5610 { 5611 if (delHours[recid] < (m_watchListBlackOut * 48) - 4) 5612 { 5613 continue; 5614 } 5615 else 5616 { 5617 // add points for a new Single or final episode 5618 if (hrs < 36) 5619 score += baseValue * (36 - hrs) / 36; 5620 5621 if (avgd != 100) 5622 { 5623 if (maxEpisodes[recid] > 0) 5624 score += (baseValue / 2) + (hrs / 24); 5625 else 5626 score += (baseValue / 3) + (baseValue * hrs / 24 / 4); 5627 } 5628 else if ((hrs / 24) < m_watchListMaxAge) 5629 score += hrs / 24; 5630 else 5631 score += m_watchListMaxAge; 5632 } 5633 } 5634 5635 // Factor based on the average time shift delay. 5636 // Scale the avgd range of 0 thru 200 hours to 133% thru 67% 5637 int delaypct = avgd / 3 + 67; 5638 5639 if (avgd < 100) 5640 score = score * (200 - delaypct) / 100; 5641 else if (avgd > 100) 5642 score = score * 100 / delaypct; 5643 5644 // use score as primary key in top 32 bits, 5645 // use age in secs as a secondary key in low 32 bits to ensure equal 5646 // scores are ordered oldest first. Copes with progs up to 136 yrs old 5647 score_type longScore = (static_cast<score_type>(score) << 32) 5648 | p->GetScheduledStartTime().secsTo(now); 5649 5650 ordered.insert(longScore, p); 5651 5652 LOG(VB_GUI, LOG_DEBUG, QString("Watchlist:%1 %2 %3 %4") 5653 .arg(score, 5) 5654 .arg(longScore, 12) 5655 .arg(p->GetTitle()) 5656 .arg(MythDate::toString(p->GetScheduledStartTime(), 5657 MythDate::kDateShort))); 5658 } 5659 return ordered; 5660 } 5661 5662 /// Sort programs oldest first, with new at top and relegating oldest 5663 WatchList::ProgramOrder WatchList::OrderByOldestStrategy() 5664 { 5665 QDateTime now = MythDate::current(); 5666 5667 QMap<int, int> avgDelay; 5668 QMap<int, QDateTime> lastDelete; 5669 5670 MSqlQuery query(MSqlQuery::InitCon()); 5671 query.prepare("SELECT recordid, avg_delay, last_delete FROM record;"); 5672 5673 if (query.exec()) 5674 { 5675 while (query.next()) 5676 { 5677 int recid = query.value(0).toInt(); 5678 avgDelay[recid] = query.value(1).toInt(); 5679 lastDelete[recid] = query.value(2).toDateTime(); 5680 } 5681 } 5682 5683 ProgramOrder ordered; 5684 foreach (const WatchGroup &group, m_list) 5685 { 5686 ProgramInfo* p = group.GetFirst(); 5687 5688 int recid = p->GetRecordingRuleID(); 5689 bool knownRule = avgDelay.contains(recid); 5690 5691 // use priority as primary key in top 32 bits: 5692 // 0 = bottom, 1 = middle, 2 = top 5693 // use age in secs as a secondary key in low 32 bits to ensure equal 5694 // scores are ordered oldest first. Copes with progs up to 136 yrs old 5695 score_type score = p->GetScheduledStartTime().secsTo(now); 5696 5697 // put new shows or those from rules that are watched quickly, at the top 5698 int ageInHours = p->GetScheduledEndTime().secsTo(now) / 3600; 5699 if (ageInHours <= m_watchListRecentLimit 5700 || (knownRule && avgDelay[recid] <= m_watchListRecentLimit)) 5701 { 5702 score |= 0x0200000000; 5703 5704 LOG(VB_GUI, LOG_DEBUG, 5705 QString("Watchlist: Top :%1 - '%2' was recorded %3 hrs " 5706 "ago & being watched after %4 hrs") 5707 .arg(score, 11) 5708 .arg(p->GetTitle()) 5709 .arg(ageInHours) 5710 .arg(avgDelay[recid])); 5711 } 5712 // put part-watched shows at top 5713 else if (p->GetProgressPercent() > 0) 5714 { 5715 score |= 0x0200000000; 5716 5717 LOG(VB_GUI, LOG_DEBUG, 5718 QString("Watchlist: Top :%1 - '%2' is %3% watched") 5719 .arg(score, 11) 5720 .arg(p->GetTitle()) 5721 .arg(p->GetProgressPercent())); 5722 } 5723 // shows go to middle if not yet old enough 5724 else if (ageInHours / 24 <= m_watchListOldLimit) 5725 { 5726 score |= 0x0100000000; 5727 5728 LOG(VB_GUI, LOG_DEBUG, 5729 QString("Watchlist: Middle:%1 - '%2' was recorded %3 hrs ago") 5730 .arg(score, 11) 5731 .arg(p->GetTitle()) 5732 .arg(ageInHours)); 5733 } 5734 // or a previous episode for the rule was deleted recently 5735 else if (knownRule) 5736 { 5737 QDateTime deleted = MythDate::as_utc(lastDelete[recid]); 5738 if (deleted.isValid() 5739 && deleted.secsTo(now) / 3600 / 24 <= m_watchListOldLimit) 5740 { 5741 score |= 0x0100000000; 5742 5743 LOG(VB_GUI, LOG_DEBUG, 5744 QString("Watchlist: Middle:%1 - '%2' was watched %3 days ago") 5745 .arg(score, 11) 5746 .arg(p->GetTitle()) 5747 .arg(deleted.secsTo(now) / 3600 / 24)); 5748 } 5749 } 5750 else 5751 LOG(VB_GUI, LOG_DEBUG, 5752 QString("Watchlist: Bottom:%1 - '%2' is %3 hrs old") 5753 .arg(score, 11) 5754 .arg(p->GetTitle()) 5755 .arg(ageInHours)); 5756 5757 ordered.insert(score, p); 5758 } 5759 return ordered; 5760 } 5761 5645 5762 /* vim: set expandtab tabstop=4 shiftwidth=4: */ -
mythtv/programs/mythfrontend/playbackbox.h
diff --git a/mythtv/programs/mythfrontend/playbackbox.h b/mythtv/programs/mythfrontend/playbackbox.h index 280a516..b01d13a 100644
a b enum { 57 57 kArtworkCoverTimeout = 50, 58 58 }; 59 59 60 class WatchGroup 61 { 62 public: 63 WatchGroup() : m_count(0), m_season(NULL), m_episode(NULL), m_date(NULL) {} 64 65 ProgramInfo* GetFirst() const; 66 uint GetCount() const { return m_count; } 67 QString GetTotal() const { return m_count > 1 ? QString::number(m_count) : ""; } 68 69 void AddSeason(ProgramInfo *pginfo) { Add(m_season, pginfo); } 70 void AddEpisode(ProgramInfo *pginfo) { Add(m_episode, pginfo); } 71 void AddDate(ProgramInfo *pginfo) { Add(m_date, pginfo); } 72 73 private: 74 void Add(ProgramInfo *¤t, ProgramInfo *pginfo); 75 76 /// Number of episodes with this title 77 uint m_count; 78 /// Oldest episode with a season defined 79 ProgramInfo *m_season; 80 /// Oldest episode with only an episode defined 81 ProgramInfo *m_episode; 82 /// Oldest episode with no season or season defined 83 ProgramInfo *m_date; 84 }; 85 86 class WatchList 87 { 88 public: 89 static QString GroupOf(const ProgramInfo &pginfo); 90 91 WatchList(); 92 93 void Clear() { m_list.clear(); } 94 bool Empty() const { return m_list.empty(); } 95 QString GetTotal(const ProgramInfo &pginfo) const 96 { return m_list[GroupOf(pginfo)].GetTotal(); } 97 void Add(ProgramInfo *p); 98 99 typedef unsigned long long score_type; // 64 bit 100 // shows keyed by score 101 typedef QMultiMap<score_type, ProgramInfo*> ProgramOrder; 102 ProgramOrder Order(); 103 104 private: 105 QMap<QString, WatchGroup> m_list; 106 /// exclude recording not marked for auto-expire from the Watch List 107 bool m_watchListAutoExpire; 108 /// add 1 to the Watch List score up to this many days 109 int m_watchListMaxAge; 110 /// adjust exclusion of a title from the Watch List after a delete 111 int m_watchListBlackOut; 112 /// recordings younger than this (hrs) go to the top 113 int m_watchListRecentLimit; 114 /// recordings older than this got to the bottom 115 int m_watchListOldLimit; 116 /// strategy for ordering the watchlist 117 QString m_watchListStrategy; 118 119 ProgramOrder OrderByClassicStrategy(); 120 ProgramOrder OrderByOldestStrategy(); 121 }; 122 60 123 class PlaybackBox : public ScheduleCommon 61 124 { 62 125 Q_OBJECT … … class PlaybackBox : public ScheduleCommon 364 427 bool m_useRecGroups; 365 428 /// use the Watch List as the initial view 366 429 bool m_watchListStart; 367 /// exclude recording not marked for auto-expire from the Watch List368 bool m_watchListAutoExpire;369 /// add 1 to the Watch List scord up to this many days370 int m_watchListMaxAge;371 /// adjust exclusion of a title from the Watch List after a delete372 int m_watchListBlackOut;373 430 /// allOrder controls the ordering of the "All Programs" list 374 431 int m_allOrder; 375 432 /// listOrder controls the ordering of the recordings in the list … … class PlaybackBox : public ScheduleCommon 384 441 QString m_watchGroupLabel; 385 442 ViewMask m_viewMask; 386 443 444 // Watchlist support 445 WatchList m_watchlist; 446 387 447 // Popup support ////////////////////////////////////////////////////////// 388 448 // General popup support 389 449 MythDialogBox *m_menuDialog; … … class PlaybackBox : public ScheduleCommon 412 472 /// Recording[s] currently selected for deletion 413 473 QStringList m_delList; 414 474 /// Group currently selected 415 QString m_currentGroup; 475 QString m_currentGroup; // in lower case 416 476 417 477 // Play List support 418 478 QList<uint> m_playList; ///< list of selected items "play list" -
mythtv/programs/mythfrontend/progdetails.cpp
diff --git a/mythtv/programs/mythfrontend/progdetails.cpp b/mythtv/programs/mythfrontend/progdetails.cpp index 3c07ed1..3204624 100644
a b void ProgDetails::loadPage(void) 589 589 QString lastRecorded; 590 590 QString nextRecording; 591 591 QString averageTimeShift; 592 QString watchListScore;593 QString watchListStatus;594 592 QString searchPhrase; 595 593 596 594 if (m_progInfo.GetRecordingRuleID()) … … void ProgDetails::loadPage(void) 619 617 averageTimeShift = tr("%n hour(s)", "", 620 618 query.value(2).toInt()); 621 619 } 622 if (recorded)623 {624 if (m_progInfo.GetRecordingPriority2() > 0)625 watchListScore =626 QString::number(m_progInfo.GetRecordingPriority2());627 628 if (m_progInfo.GetRecordingPriority2() < 0)629 {630 switch (m_progInfo.GetRecordingPriority2())631 {632 case wlExpireOff:633 watchListStatus = tr("Auto-expire off");634 break;635 case wlWatched:636 watchListStatus = tr("Marked as 'watched'");637 break;638 case wlEarlier:639 watchListStatus = tr("Not the earliest episode");640 break;641 case wlDeleted:642 watchListStatus = tr("Recently deleted episode");643 break;644 }645 }646 }647 620 if (record->m_searchType != kManualSearch && 648 621 record->m_description != m_progInfo.GetDescription()) 649 622 { … … void ProgDetails::loadPage(void) 655 628 addItem("LAST_RECORDED", tr("Last Recorded"), lastRecorded); 656 629 addItem("NEXT_RECORDING", tr("Next Recording"), nextRecording); 657 630 addItem("AVERAGE_TIME_SHIFT", tr("Average Time Shift"), averageTimeShift); 658 addItem("WATCH_LIST_SCORE", tr("Watch List Score"), watchListScore); 659 addItem("WATCH_LIST_STATUS", tr("Watch List Status"), watchListStatus); 631 // Blank removed labels until all themes have removed it 632 addItem("WATCH_LIST_SCORE", "",""); 633 addItem("WATCH_LIST_STATUS", "",""); 660 634 addItem("SEARCH_PHRASE", tr("Search Phrase"), searchPhrase); 661 635 662 636 s.clear(); -
mythtv/programs/mythfrontend/programinfocache.cpp
diff --git a/mythtv/programs/mythfrontend/programinfocache.cpp b/mythtv/programs/mythfrontend/programinfocache.cpp index 5a8c59f..f217f09 100644
a b uint32_t ProgramInfoCache::Update(const ProgramInfo& pginfo) 248 248 if (pginfo.GetRecordingGroup() != pg.GetRecordingGroup()) 249 249 flags |= PIC_RECGROUP_CHANGED; 250 250 251 if (pginfo.GetSeason() != pg.GetSeason() 252 || pginfo.GetEpisode() != pg.GetEpisode() 253 || pginfo.GetTitle() != pg.GetTitle()) 254 flags |= PIC_WATCHLIST_CHANGED; 255 256 if (pg.GetProgressPercent() > 0) 257 flags |= PIC_PART_WATCHED; 258 251 259 pg.clone(pginfo, true); 252 260 pg.SetAllowLastPlayPos(true); 253 261 … … void ProgramInfoCache::UpdateFileSize(uint recordingId, uint64_t filesize, 295 303 QString byWhom; 296 304 if (pg.QueryIsInUse(byWhom) && byWhom.contains(QObject::tr("Playing"))) 297 305 flags &= ~PIC_MARK_CHANGED; 306 307 // Changing to or from part-watched may affect watchlist 308 if ((pg.GetProgressPercent() == 0) != !(flags & PIC_PART_WATCHED)) 309 flags |= PIC_WATCHLIST_CHANGED; 298 310 } 299 311 300 312 QString mesg = QString("UPDATE_UI_ITEM %1 %2").arg(recordingId).arg(flags); -
mythtv/programs/mythfrontend/programinfocache.h
diff --git a/mythtv/programs/mythfrontend/programinfocache.h b/mythtv/programs/mythfrontend/programinfocache.h index 4c8d5a4..4dbde6c 100644
a b typedef enum { 24 24 PIC_NONE = 0x00, 25 25 PIC_MARK_CHANGED = 0x01, 26 26 PIC_RECGROUP_CHANGED = 0x02, 27 PIC_PART_WATCHED = 0X04, 28 PIC_WATCHLIST_CHANGED = 0x08, 27 29 PIC_NO_ACTION = 0x80, 28 30 } UpdateState; 29 31 -
mythtv/themes/MythCenter-wide/htmls/progdetails_page2.html
diff --git a/mythtv/themes/MythCenter-wide/htmls/progdetails_page2.html b/mythtv/themes/MythCenter-wide/htmls/progdetails_page2.html index e35b5fc..98567da 100644
a b 32 32 <h1>%LAST_RECORDED_LABEL%</h1> <p>%LAST_RECORDED%</p> 33 33 <h1>%NEXT_RECORDING_LABEL%</h1> <p>%NEXT_RECORDING%</p> 34 34 <h1>%AVERAGE_TIME_SHIFT_LABEL%</h1> <p>%AVERAGE_TIME_SHIFT%</p> 35 <h1>%WATCH_LIST_SCORE_LABEL%</h1> <p>%WATCH_LIST_SCORE%</p>36 <h1>%WATCH_LIST_STATUS_LABEL%</h1> <p>%WATCH_LIST_STATUS%</p>37 35 <h1>%SEARCH_PHRASE_LABEL%</h1> <p>%SEARCH_PHRASE%</p> 38 36 <h1>%FINDID_LABEL%</h1> <p>%FINDID%</p> 39 37 <h1>%RECORDING_HOST_LABEL%</h1> <p>%RECORDING_HOST%</p> -
mythtv/themes/MythCenter-wide/recordings-ui.xml
diff --git a/mythtv/themes/MythCenter-wide/recordings-ui.xml b/mythtv/themes/MythCenter-wide/recordings-ui.xml index 569e204..0abbb29 100644
a b 222 222 <textarea name="titlesubtitle" from="buttontext"> 223 223 <area>32,2,656,28</area> 224 224 <align>vcenter</align> 225 <template>%title subtitle%% (|progresspercent|%)%</template>225 <template>%title%% (|watchtotal|)%% - "|subtitle|"%</template> 226 226 </textarea> 227 227 <textarea name="shortstartdate" from="buttontext"> 228 228 <area>634,2,120,28</area> … … 271 271 </textarea> 272 272 <textarea name="titlesubtitle" from="fonts"> 273 273 <area>32,2,656,28</area> 274 <template>%title subtitle%% (|progresspercent|%)%</template>274 <template>%title%% (|watchtotal|)%% - "|subtitle|"%</template> 275 275 </textarea> 276 276 <textarea name="shortstartdate" from="fonts"> 277 277 <area>634,2,120,28</area> -
mythtv/themes/MythCenter/htmls/progdetails_page2.html
diff --git a/mythtv/themes/MythCenter/htmls/progdetails_page2.html b/mythtv/themes/MythCenter/htmls/progdetails_page2.html index 2fa3ea3..af6199d 100644
a b 34 34 <h1>%LAST_RECORDED_LABEL%</h1> <p>%LAST_RECORDED%</p> 35 35 <h1>%NEXT_RECORDING_LABEL%</h1> <p>%NEXT_RECORDING%</p> 36 36 <h1>%AVERAGE_TIME_SHIFT_LABEL%</h1> <p>%AVERAGE_TIME_SHIFT%</p> 37 <h1>%WATCH_LIST_SCORE_LABEL%</h1> <p>%WATCH_LIST_SCORE%</p>38 <h1>%WATCH_LIST_STATUS_LABEL%</h1> <p>%WATCH_LIST_STATUS%</p>39 37 <h1>%SEARCH_PHRASE_LABEL%</h1> <p>%SEARCH_PHRASE%</p> 40 38 <h1>%FINDID_LABEL%</h1> <p>%FINDID%</p> 41 39 <h1>%RECORDING_HOST_LABEL%</h1> <p>%RECORDING_HOST%</p> -
mythtv/themes/MythCenter/recordings-ui.xml
diff --git a/mythtv/themes/MythCenter/recordings-ui.xml b/mythtv/themes/MythCenter/recordings-ui.xml index 5500fd7..3c6e418 100644
a b 212 212 <textarea name="titlesubtitle" from="buttontext"> 213 213 <area>32,0,336,30</area> 214 214 <align>vcenter</align> 215 <template>%title%% (|watchtotal|)%% - "|subtitle|"%</template> 215 216 </textarea> 216 <textarea name="shortstartdate" from=" titlesubtitle">217 <textarea name="shortstartdate" from="buttontext"> 217 218 <area>295,0,130,30</area> 218 219 <align>right,vcenter</align> 219 220 </textarea> … … 249 250 <shape name="selectbar"> 250 251 <area>0,0,100%,30</area> 251 252 </shape> 252 <textarea name="titlesubtitle" from="buttontext"> 253 <area>32,0,336,30</area> 253 <textarea name="fonts" from="buttontext"> 254 254 <font>basesmall_normal_selected</font> 255 255 <font state="disabled">basesmall_disabled_selected</font> 256 256 <font state="error">basesmall_error_selected</font> 257 257 <font state="warning">basesmall_warning_selected</font> 258 258 <font state="normal">basesmall_normal_selected</font> 259 259 <font state="running">basesmall_running_selected</font> 260 </textarea> 261 <textarea name="titlesubtitle" from="fonts"> 262 <area>32,0,336,30</area> 260 263 <align>vcenter</align> 264 <template>%title%% (|watchtotal|)%% - "|subtitle|"%</template> 261 265 </textarea> 262 <textarea name="shortstartdate" from=" titlesubtitle">266 <textarea name="shortstartdate" from="fonts"> 263 267 <area>295,0,130,30</area> 264 268 <align>right,vcenter</align> 265 269 </textarea> 266 <textarea name="starttime" from=" shortstartdate">270 <textarea name="starttime" from="fonts"> 267 271 <area>415,0,114,30</area> 268 272 <align>right,vcenter</align> 269 273 </textarea> -
mythtv/themes/Terra/htmls/progdetails_page2.html
diff --git a/mythtv/themes/Terra/htmls/progdetails_page2.html b/mythtv/themes/Terra/htmls/progdetails_page2.html index a1426d6..02963cd 100644
a b 36 36 <h1>%LAST_RECORDED_LABEL%</h1> <p>%LAST_RECORDED%</p> 37 37 <h1>%NEXT_RECORDING_LABEL%</h1> <p>%NEXT_RECORDING%</p> 38 38 <h1>%AVERAGE_TIME_SHIFT_LABEL%</h1> <p>%AVERAGE_TIME_SHIFT%</p> 39 <h1>%WATCH_LIST_SCORE_LABEL%</h1> <p>%WATCH_LIST_SCORE%</p>40 <h1>%WATCH_LIST_STATUS_LABEL%</h1> <p>%WATCH_LIST_STATUS%</p>41 39 <h1>%SEARCH_PHRASE_LABEL%</h1> <p>%SEARCH_PHRASE%</p> 42 40 <h1>%FINDID_LABEL%</h1> <p>%FINDID%</p> 43 41 <h1>%RECORDING_HOST_LABEL%</h1> <p>%RECORDING_HOST%</p> -
mythtv/themes/Terra/recordings-ui.xml
diff --git a/mythtv/themes/Terra/recordings-ui.xml b/mythtv/themes/Terra/recordings-ui.xml index 195ae08..9f160d8 100644
a b 134 134 </imagetype> 135 135 </state> 136 136 </statetype> 137 <textarea name="watchtotal" from="basetextarea"> 138 <area>130,121,120,25</area> 139 <align>right</align> 140 </textarea> 137 141 <textarea name="title" from="basetextarea"> 138 142 <area>8,148,240,60</area> 139 143 <font>basemedium</font> … … 166 170 <statetype name="status"> 167 171 <position>236,124</position> 168 172 </statetype> 173 <textarea name="watchtotal"> 174 <area>158,144,120,25</area> 175 </textarea> 169 176 <textarea name="title"> 170 177 <area>8,169,272,73</area> 171 178 <font>baselarge</font> -
mythtv/themes/default/htmls/progdetails_page2.html
diff --git a/mythtv/themes/default/htmls/progdetails_page2.html b/mythtv/themes/default/htmls/progdetails_page2.html index 40fb94b..4a17980 100644
a b 33 33 <h1>%LAST_RECORDED_LABEL%</h1> <p>%LAST_RECORDED%</p> 34 34 <h1>%NEXT_RECORDING_LABEL%</h1> <p>%NEXT_RECORDING%</p> 35 35 <h1>%AVERAGE_TIME_SHIFT_LABEL%</h1> <p>%AVERAGE_TIME_SHIFT%</p> 36 <h1>%WATCH_LIST_SCORE_LABEL%</h1> <p>%WATCH_LIST_SCORE%</p>37 <h1>%WATCH_LIST_STATUS_LABEL%</h1> <p>%WATCH_LIST_STATUS%</p>38 36 <h1>%SEARCH_PHRASE_LABEL%</h1> <p>%SEARCH_PHRASE%</p> 39 37 <h1>%FINDID_LABEL%</h1> <p>%FINDID%</p> 40 38 <h1>%RECORDING_HOST_LABEL%</h1> <p>%RECORDING_HOST%</p>
