Index: libs/libmythtv/previewgeneratorqueue.cpp =================================================================== --- libs/libmythtv/previewgeneratorqueue.cpp (revision 26449) +++ libs/libmythtv/previewgeneratorqueue.cpp (working copy) @@ -273,8 +273,13 @@ bool bookmark_updated = false; QDateTime bookmark_ts = pginfo.QueryBookmarkTimeStamp(); - QDateTime cmp_ts = bookmark_ts.isValid() ? - bookmark_ts : pginfo.GetLastModifiedTime(); + QDateTime cmp_ts; + if (bookmark_ts.isValid()) + cmp_ts = bookmark_ts; + else if (QDateTime::currentDateTime() >= pginfo.GetRecordingEndTime()) + cmp_ts = pginfo.GetLastModifiedTime(); + else + cmp_ts = pginfo.GetRecordingStartTime(); if (streaming) { @@ -384,8 +389,9 @@ else if (needs_gen) { VERBOSE(VB_PLAYBACK, LOC + - "Not requesting preview as it " - "is already being generated"); + QString("Not requesting preview for %1," + "as it is already being generated") + .arg(pginfo.toString(ProgramInfo::kTitleSubtitle))); IncPreviewGeneratorPriority(key, token); } Index: libs/libmythui/mythuibuttonlist.cpp =================================================================== --- libs/libmythui/mythuibuttonlist.cpp (revision 26449) +++ libs/libmythui/mythuibuttonlist.cpp (working copy) @@ -2894,7 +2894,7 @@ newText = text->GetDefaultText(); QRegExp regexp("%(\\|(.))?([^\\|]+)(\\|(.))?%"); regexp.setMinimal(true); - if (newText.contains(regexp)) + if (!newText.isEmpty() && newText.contains(regexp)) { int pos = 0; QString tempString = newText; Index: programs/mythfrontend/playbackbox.cpp =================================================================== --- programs/mythfrontend/playbackbox.cpp (revision 26449) +++ programs/mythfrontend/playbackbox.cpp (working copy) @@ -784,9 +784,18 @@ item->DisplayState(rating, "ratingstate"); - QString oldimgfile = item->GetImage("preview"); + // If there is no preview image, load it. + QString oldimgfile = GetLoadedPreview(*pginfo); if (oldimgfile.isEmpty() || force_preview_reload) m_preview_tokens.insert(m_helper.GetPreviewImage(*pginfo)); + // Try to set preview image + if (oldimgfile.isEmpty()) + oldimgfile = GetStalePreview(*pginfo); + if (item->GetImage("preview").isEmpty()) + { + if (!oldimgfile.isEmpty()) + item->SetImage(oldimgfile, "preview", false); + } if ((GetFocusWidget() == m_recordingList) && is_sel) { @@ -810,7 +819,7 @@ if (m_previewImage) { m_previewImage->SetFilename(oldimgfile); - m_previewImage->Load(true, true); + m_previewImage->Load(false, false); } // Handle artwork @@ -849,33 +858,103 @@ void PlaybackBox::ItemVisible(MythUIButtonListItem *item) { ProgramInfo *pginfo = qVariantValue(item->GetData()); + if (!pginfo) + return; // Job status (recording, transcoding, flagging) QString job = extract_job_state(*pginfo); item->DisplayState(job, "jobstate"); MythUIButtonListItem *sel_item = item->parent()->GetItemCurrent(); - if ((item != sel_item) && pginfo && item->GetImage("preview").isEmpty() && - (asAvailable == pginfo->GetAvailableStatus())) + if (item == sel_item) + return; + + if ((asAvailable == pginfo->GetAvailableStatus()) && + GetLoadedPreview(*pginfo).isEmpty()) { QString token = m_helper.GetPreviewImage(*pginfo, true); - if (token.isEmpty()) - return; - - m_preview_tokens.insert(token); - // now make sure selected item is still at the top of the queue - ProgramInfo *sel_pginfo = - qVariantValue(sel_item->GetData()); - if (sel_pginfo && sel_item->GetImage("preview").isEmpty() && - (asAvailable == sel_pginfo->GetAvailableStatus())) + if (!token.isEmpty()) { - m_preview_tokens.insert( - m_helper.GetPreviewImage(*sel_pginfo, false)); + m_preview_tokens.insert(token); + // now make sure selected item is still at the top of the queue + ProgramInfo *sel_pginfo = + qVariantValue(sel_item->GetData()); + if (sel_pginfo && GetLoadedPreview(*sel_pginfo).isEmpty() && + (asAvailable == sel_pginfo->GetAvailableStatus())) + { + m_preview_tokens.insert( + m_helper.GetPreviewImage(*sel_pginfo, false)); + } } } + + if (item->GetImage("preview").isEmpty()) + { + QString cachedPreview = GetStalePreview(*pginfo); + if (!cachedPreview.isEmpty()) + item->SetImage(cachedPreview, "preview", true); + } } +/** \brief Returns a relatively up to date preview file name if available. + * This will only be returned after the preview generator hands us this + * preview file as an up to date preview file. This returns an empty string + * when not available. + * \sa GetStalePreview() + */ +QString PlaybackBox::GetLoadedPreview(const ProgramInfo &pginfo) const +{ + PreviewCache::const_iterator it = + m_previewCache.find(pginfo.MakeUniqueKey()); + return (it == m_previewCache.end()) ? QString() : + ((*it).left(6) == "cache:") ? QString() : *it; +} +/** \brief Returns the most up to date preview we have in local storage. + * This image may be quite stale, as another frontent may have changed + * the file or the bookmarks since the last time it was shown on screen + * on this frontend. This is meant as a temporary placeholder image. + * \sa GetLoadedPreview() + */ +QString PlaybackBox::GetStalePreview(const ProgramInfo &pginfo) const +{ + QString key = pginfo.MakeUniqueKey(); + PreviewCache::const_iterator it = m_previewCache.find(key); + QString ret; + if (it != m_previewCache.end()) + ret = ((*it).left(6) == "cache:") ? (*it).mid(6) : *it; + if (ret.left(12) == "") + return QString(); + else if (ret.length()) + { + VERBOSE(VB_IMPORTANT, QString("GetStalePreview() 1 -> '%1'") + .arg(ret)); + return ret; + } + + QString preview; + QString remotecachedirname = QString("%1/remotecache").arg(GetConfDir()); + QDir remotecachedir(remotecachedirname); + if (remotecachedir.exists()) + { + QString filename = QString("%1/%2") + .arg(remotecachedirname) + .arg(pginfo.GetBasename() + ".png"); + if (QFileInfo(filename).isReadable()) + preview = filename; + } + + ret = m_previewCache[key] = QString("cache:") + + ((preview.isEmpty()) ? QString("") : preview); + ret = (ret.left(6) == "cache:") ? (ret).mid(6) : ret; + + if (ret.left(12) != "") + VERBOSE(VB_IMPORTANT, QString("GetStalePreview() 2 -> '%1'") + .arg(ret)); + + return (ret.left(12) == "") ? QString() : ret; +} + /** \brief Updates the UI properties for a new preview file. * This first update the image property of the MythUIButtonListItem * with the new preview file, then if it is selected and there is @@ -946,6 +1025,8 @@ QString("Loading preview %1,\n\t\t\tmsg %2") .arg(previewFile).arg(message)); + m_previewCache[info->MakeUniqueKey()] = previewFile; + item->SetImage(previewFile, "preview", true); if ((GetFocusWidget() == m_recordingList) && Index: programs/mythfrontend/playbackboxhelper.cpp =================================================================== --- programs/mythfrontend/playbackboxhelper.cpp (revision 26449) +++ programs/mythfrontend/playbackboxhelper.cpp (working copy) @@ -602,6 +602,9 @@ QString PlaybackBoxHelper::GetPreviewImage( const ProgramInfo &pginfo, bool check_availability) { + VERBOSE(VB_IMPORTANT, QString("GetPreviewImage(%1)") + .arg(pginfo.GetBasename())); + if (!check_availability && pginfo.GetAvailableStatus() != asAvailable) return QString(); Index: programs/mythfrontend/playbackbox.h =================================================================== --- programs/mythfrontend/playbackbox.h (revision 26449) +++ programs/mythfrontend/playbackbox.h (working copy) @@ -45,6 +45,7 @@ class MythDialogBox; typedef QMap ProgramMap; +typedef QMap PreviewCache; typedef QMap Str2StrMap; enum { @@ -327,6 +328,9 @@ QString CreateProgramInfoString(const ProgramInfo &program) const; + QString GetLoadedPreview(const ProgramInfo &pginfo) const; + QString GetStalePreview(const ProgramInfo &pginfo) const; + private: QRegExp m_prefixes; ///< prefixes to be ignored when sorting QRegExp m_titleChaff; ///< stuff to remove for search rules @@ -398,6 +402,7 @@ // Main Recording List support QStringList m_titleList; ///< list of pages ProgramMap m_progLists; ///< lists of programs by page + mutable PreviewCache m_previewCache; int m_progsInDB; ///< total number of recordings in DB bool m_isFilling;