MythTV master
programinfocache.cpp
Go to the documentation of this file.
1// -*- Mode: c++ -*-
2// vim:set sw=4 ts=4 expandtab:
3// Copyright (c) 2009, Daniel Thor Kristjansson
4// Distributed as part of MythTV under GPL version 2
5// (or at your option a later version)
6
7// C++
8#include <algorithm>
9
10// Qt
11#include <QCoreApplication>
12#include <QRunnable>
13
14// MythTV
17#include "libmythbase/mythdb.h"
22
23// MythFrontend
24#include "programinfocache.h"
25
26using VPI_ptr = std::vector<ProgramInfo *> *;
27static void free_vec(VPI_ptr &v)
28{
29 if (v)
30 {
31 for (auto & it : *v)
32 delete it;
33 delete v;
34 v = nullptr;
35 }
36}
37
38class ProgramInfoLoader : public QRunnable
39{
40 public:
41 ProgramInfoLoader(ProgramInfoCache &c, const bool updateUI)
42 : m_cache(c), m_updateUI(updateUI) {}
43
44 void run(void) override // QRunnable
45 {
47 }
48
51};
52
54{
55 QMutexLocker locker(&m_lock);
56
57 while (m_loadsInProgress)
58 m_loadWait.wait(&m_lock);
59
60 Clear();
62}
63
64void ProgramInfoCache::ScheduleLoad(const bool updateUI)
65{
66 QMutexLocker locker(&m_lock);
67 if (!m_loadIsQueued)
68 {
69 m_loadIsQueued = true;
72 new ProgramInfoLoader(*this, updateUI), "ProgramInfoLoader");
73 }
74}
75
76void ProgramInfoCache::Load(const bool updateUI)
77{
78 QMutexLocker locker(&m_lock);
79 m_loadIsQueued = false;
80
81 locker.unlock();
82
83 // Get an unsorted list (sort = 0) from RemoteGetRecordedList
84 // we sort the list later anyway.
85 std::vector<ProgramInfo*> *tmp = RemoteGetRecordedList(0);
86
87 // Calculate play positions for UI
88 if (tmp)
89 {
90 // Played progress
91 using ProgId = QPair<uint, QDateTime>;
92 QHash<ProgId, uint> lastPlayFrames;
93
94 // Get all lastplaypos marks in a single lookup
96 query.prepare("SELECT chanid, starttime, mark "
97 "FROM recordedmarkup "
98 "WHERE type = :TYPE ");
99 query.bindValue(":TYPE", MARK_UTIL_LASTPLAYPOS);
100
101 if (query.exec())
102 {
103 while (query.next())
104 {
105 ProgId id = qMakePair(query.value(0).toUInt(),
106 MythDate::as_utc(query.value(1).toDateTime()));
107 lastPlayFrames[id] = query.value(2).toUInt();
108 }
109
110 // Determine progress of each prog
111 for (ProgramInfo* pg : *tmp)
112 {
113 ProgId id = qMakePair(pg->GetChanID(),
114 pg->GetRecordingStartTime());
115 pg->CalculateProgress(lastPlayFrames.value(id));
116 }
117 }
118 else
119 {
120 MythDB::DBError("Watched progress", query);
121 }
122 }
123
124 locker.relock();
125
128
129 if (updateUI)
130 QCoreApplication::postEvent(
131 m_listener, new MythEvent("UPDATE_UI_LIST"));
132
134 m_loadWait.wakeAll();
135}
136
138{
139 QMutexLocker locker(&m_lock);
140 return m_loadsInProgress != 0U;
141}
142
144{
145 QMutexLocker locker(&m_lock);
146 while (m_loadsInProgress)
147 m_loadWait.wait(&m_lock);
148}
149
161{
162 QMutexLocker locker(&m_lock);
163 if (m_nextCache)
164 {
165 Clear();
166 for (auto & it : *m_nextCache)
167 {
168 if (!it->GetChanID())
169 continue;
170
171 if (m_cache.contains(it->GetRecordingID()))
172 {
173 // An entry using that key already exists in hash.
174 // Free allocated memory for the entry to be replaced.
175 delete m_cache[it->GetRecordingID()];
176 }
177
178 m_cache[it->GetRecordingID()] = it;
179 }
180 delete m_nextCache;
181 m_nextCache = nullptr;
182 return;
183 }
184
185 for (auto it = m_cache.begin(); it != m_cache.end(); )
186 {
187 if ((*it)->GetAvailableStatus() == asDeleted)
188 {
189 delete (*it);
190 it = m_cache.erase(it);
191 }
192 else
193 {
194 it++;
195 }
196 }
197}
198
203ProgramInfoCache::UpdateStates ProgramInfoCache::Update(const ProgramInfo &pginfo)
204{
205 QMutexLocker locker(&m_lock);
206
207 uint recordingId = pginfo.GetRecordingID();
208 Cache::iterator it = m_cache.find(recordingId);
209
210 if (it == m_cache.end())
211 return PIC_NO_ACTION;
212
213 ProgramInfo& pg = **it;
214 UpdateStates flags { PIC_NONE };
215
216 if (pginfo.GetBookmarkUpdate() != pg.m_previewUpdate)
217 flags |= PIC_MARK_CHANGED;
218
219 if (pginfo.GetRecordingGroup() != pg.GetRecordingGroup())
220 flags |= PIC_RECGROUP_CHANGED;
221
222 pg.clone(pginfo, true);
223
224 if (flags & PIC_MARK_CHANGED)
225 {
226 // Delegate this update to a background task
228 recordingId, 0, flags);
229
230 // Ignore this update
231 flags = PIC_NO_ACTION;
232 }
233
234 LOG(VB_GUI, LOG_DEBUG, QString("Pg %1 %2 update state %3")
235 .arg(recordingId).arg(pg.GetTitle()).arg(flags));
236 return flags;
237}
238
243void ProgramInfoCache::UpdateFileSize(uint recordingID, uint64_t filesize,
244 UpdateStates flags)
245{
246 Cache::iterator it = m_cache.find(recordingID);
247 if (it == m_cache.end())
248 return;
249
250 ProgramInfo *pg = *it;
251
253
254 if (filesize > 0)
255 {
256 // Filesize update
257 pg->SetFilesize(filesize);
258 pg->SetAvailableStatus(asAvailable, "PIC::UpdateFileSize");
259 }
260 else // Info update
261 {
262 // Don't keep regenerating previews of files being played
263 QString byWhom;
264 if (pg->QueryIsInUse(byWhom) && byWhom.contains(QObject::tr("Playing")))
265 flags &= ~PIC_MARK_CHANGED;
266 }
267
268 // Time of preview picture generation request for next comparison
269 if (flags & PIC_MARK_CHANGED)
271
272 QString mesg = QString("UPDATE_UI_ITEM %1 %2").arg(recordingID).arg(flags);
273 QCoreApplication::postEvent(m_listener, new MythEvent(mesg));
274
275 LOG(VB_GUI, LOG_DEBUG, mesg);
276}
277
282{
283 if (!pginfo.GetRecordingID() || (Update(pginfo) != PIC_NO_ACTION))
284 return;
285
286 m_cache[pginfo.GetRecordingID()] = new ProgramInfo(pginfo);
287}
288
295{
296 Cache::iterator it = m_cache.find(recordingID);
297
298 if (it != m_cache.end())
299 (*it)->SetAvailableStatus(asDeleted, "PIC::Remove");
300
301 return it != m_cache.end();
302}
303
304// two helper functions that are used only in this file
305namespace {
306 // Sorting functions for ProgramInfoCache::GetOrdered()
307 bool PISort(const ProgramInfo *a, const ProgramInfo *b)
308 {
310 return a->GetChanID() < b->GetChanID();
311 return (a->GetRecordingStartTime() < b->GetRecordingStartTime());
312 }
313
314 bool reversePISort(const ProgramInfo *a, const ProgramInfo *b)
315 {
317 return a->GetChanID() > b->GetChanID();
318 return (a->GetRecordingStartTime() > b->GetRecordingStartTime());
319 }
320}
321
322void ProgramInfoCache::GetOrdered(std::vector<ProgramInfo*> &list, bool newest_first)
323{
324 std::copy(m_cache.cbegin(), m_cache.cend(), std::back_inserter(list));
325
326 if (newest_first)
327 std::sort(list.begin(), list.end(), reversePISort);
328 else
329 std::sort(list.begin(), list.end(), PISort);
330
331}
332
334{
335 Cache::const_iterator it = m_cache.find(recordingID);
336
337 if (it != m_cache.end())
338 return *it;
339
340 return nullptr;
341}
342
345{
346 for (const auto & pi : std::as_const(m_cache))
347 delete pi;
348 m_cache.clear();
349}
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QVariant value(int i) const
Definition: mythdbcon.h:204
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
static MThreadPool * globalInstance(void)
void start(QRunnable *runnable, const QString &debugName, int priority=0)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
This class is used as a container for messages.
Definition: mythevent.h:17
void GetOrdered(std::vector< ProgramInfo * > &list, bool newest_first=false)
void Add(const ProgramInfo &pginfo)
Adds a ProgramInfo to the cache.
bool Remove(uint recordingID)
Marks a ProgramInfo in the cache for deletion on the next call to Refresh().
bool IsLoadInProgress(void) const
friend class ProgramInfoLoader
void Load(bool updateUI=true)
std::vector< ProgramInfo * > * m_nextCache
void Refresh(void)
Refreshed the cache.
void UpdateFileSize(uint recordingID, uint64_t filesize, UpdateStates flags)
Updates a ProgramInfo in the cache.
QWaitCondition m_loadWait
void ScheduleLoad(bool updateUI=true)
ProgramInfoCache::UpdateStates Update(const ProgramInfo &pginfo)
Updates a ProgramInfo in the cache.
void WaitForLoadToComplete(void) const
ProgramInfo * GetRecordingInfo(uint recordingID) const
void Clear(void)
Clears the cache, m_lock must be held when this is called.
ProgramInfoLoader(ProgramInfoCache &c, const bool updateUI)
void run(void) override
ProgramInfoCache & m_cache
Holds information on recordings and videos.
Definition: programinfo.h:68
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:373
void SetAvailableStatus(AvailableStatusType status, const QString &where)
bool QueryIsInUse(QStringList &byWho) const
Returns true if Program is in use.
QDateTime m_previewUpdate
Definition: programinfo.h:863
QDateTime GetBookmarkUpdate(void) const
Definition: programinfo.h:478
QString GetRecordingGroup(void) const
Definition: programinfo.h:420
uint GetRecordingID(void) const
Definition: programinfo.h:450
virtual void SetFilesize(uint64_t sz)
QString GetTitle(void) const
Definition: programinfo.h:362
QDateTime GetRecordingStartTime(void) const
Approximate time the recording started.
Definition: programinfo.h:405
virtual void clone(const ProgramInfo &other, bool ignore_non_serialized_data=false)
Copies important fields from other ProgramInfo.
uint64_t QueryLastPlayPos(void) const
Gets any lastplaypos position in database, unless the ignore lastplaypos flag is set.
void CalculateProgress(uint64_t pos)
unsigned int uint
Definition: freesurround.h:24
static guint32 * tmp
Definition: goom_core.cpp:26
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
void run(const QString &name, Class *object, void(Class::*fn)())
Definition: mconcurrent.h:137
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:28
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
bool reversePISort(const ProgramInfo *a, const ProgramInfo *b)
bool PISort(const ProgramInfo *a, const ProgramInfo *b)
static void free_vec(VPI_ptr &v)
std::vector< ProgramInfo * > * VPI_ptr
@ asAvailable
Definition: programtypes.h:176
@ asDeleted
Definition: programtypes.h:181
@ MARK_UTIL_LASTPLAYPOS
Definition: programtypes.h:76
std::vector< ProgramInfo * > * RemoteGetRecordedList(int sort)
Definition: remoteutil.cpp:19