MythTV  master
lookup.cpp
Go to the documentation of this file.
1 // C++
2 #include <vector>
3 
4 // Qt
5 #include <QList>
6 
7 // MythTV
11 #include "libmythbase/remoteutil.h"
12 #include "libmythtv/jobqueue.h"
15 
16 // MythMetadataLookup
17 #include "lookup.h"
18 
20  : m_metadataFactory(new MetadataFactory(this))
21 {
22 }
23 
25 {
26  while (!m_busyRecList.isEmpty())
27  delete m_busyRecList.takeFirst();
28 }
29 
31 {
32  return m_metadataFactory->IsRunning() ||
33  (m_busyRecList.count() != 0);
34 }
35 
37  const QDateTime &starttime,
38  bool updaterules)
39 {
40  auto *pginfo = new ProgramInfo(chanid, starttime);
41 
42  if (!pginfo)
43  {
44  LOG(VB_GENERAL, LOG_ERR,
45  "No valid program info for supplied chanid/starttime");
46  return;
47  }
48 
49  m_updaterules = updaterules;
50  m_updateartwork = true;
51 
52  m_busyRecList.append(pginfo);
53  m_metadataFactory->Lookup(pginfo, true, m_updateartwork, false);
54 }
55 
56 void LookerUpper::HandleAllRecordings(bool updaterules)
57 {
58  QMap< QString, ProgramInfo* > recMap;
59  QMap< QString, uint32_t > inUseMap = ProgramInfo::QueryInUseMap();
60  QMap< QString, bool > isJobRunning = ProgramInfo::QueryJobsRunning(JOB_COMMFLAG);
61 
62  m_updaterules = updaterules;
63 
64  ProgramList progList;
65 
66  LoadFromRecorded( progList, false, inUseMap, isJobRunning, recMap, -1 );
67 
68  for (auto *pg : progList)
69  {
70  auto *pginfo = new ProgramInfo(*pg);
71  if ((pginfo->GetRecordingGroup() != "Deleted") &&
72  (pginfo->GetRecordingGroup() != "LiveTV") &&
73  (pginfo->GetInetRef().isEmpty() ||
74  (!pginfo->GetSubtitle().isEmpty() &&
75  (pginfo->GetSeason() == 0) &&
76  (pginfo->GetEpisode() == 0))))
77  {
78  QString msg = QString("Looking up: %1 %2")
79  .arg(pginfo->GetTitle(), pginfo->GetSubtitle());
80  LOG(VB_GENERAL, LOG_INFO, msg);
81 
82  m_busyRecList.append(pginfo);
83  m_metadataFactory->Lookup(pginfo, true, false, false);
84  }
85  else
86  {
87  delete pginfo;
88  }
89  }
90 }
91 
93 {
94  m_updaterules = true;
95 
96  std::vector<ProgramInfo *> recordingList;
97 
98  RemoteGetAllScheduledRecordings(recordingList);
99 
100  for (auto & pg : recordingList)
101  {
102  auto *pginfo = new ProgramInfo(*pg);
103  if (pginfo->GetInetRef().isEmpty())
104  {
105  QString msg = QString("Looking up: %1 %2")
106  .arg(pginfo->GetTitle(), pginfo->GetSubtitle());
107  LOG(VB_GENERAL, LOG_INFO, msg);
108 
109  m_busyRecList.append(pginfo);
110  m_metadataFactory->Lookup(pginfo, true, false, true);
111  }
112  else
113  {
114  delete pginfo;
115  }
116  }
117 }
118 
119 void LookerUpper::HandleAllArtwork(bool aggressive)
120 {
121  m_updateartwork = true;
122 
123  if (aggressive)
124  m_updaterules = true;
125 
126  // First, handle all recording rules w/ inetrefs
127  std::vector<ProgramInfo *> recordingList;
128 
129  RemoteGetAllScheduledRecordings(recordingList);
130  int maxartnum = 3;
131 
132  for (auto & pg : recordingList)
133  {
134  auto *pginfo = new ProgramInfo(*pg);
135  bool dolookup = true;
136 
137  if (pginfo->GetInetRef().isEmpty())
138  dolookup = false;
139  if (dolookup || aggressive)
140  {
141  ArtworkMap map = GetArtwork(pginfo->GetInetRef(), pginfo->GetSeason(), true);
142  if (map.isEmpty() || (aggressive && map.count() < maxartnum))
143  {
144  QString msg = QString("Looking up artwork for recording rule: %1 %2")
145  .arg(pginfo->GetTitle(), pginfo->GetSubtitle());
146  LOG(VB_GENERAL, LOG_INFO, msg);
147 
148  m_busyRecList.append(pginfo);
149  m_metadataFactory->Lookup(pginfo, true, true, true);
150  continue;
151  }
152  }
153  delete pginfo;
154  }
155 
156  // Now, Attempt to fill in the gaps for recordings
157  QMap< QString, ProgramInfo* > recMap;
158  QMap< QString, uint32_t > inUseMap = ProgramInfo::QueryInUseMap();
159  QMap< QString, bool > isJobRunning = ProgramInfo::QueryJobsRunning(JOB_COMMFLAG);
160 
161  ProgramList progList;
162 
163  LoadFromRecorded( progList, false, inUseMap, isJobRunning, recMap, -1 );
164 
165  for (auto *pg : progList)
166  {
167  auto *pginfo = new ProgramInfo(*pg);
168 
169  bool dolookup = true;
170 
171  LookupType type = GuessLookupType(pginfo);
172 
173  if (type == kProbableMovie)
174  maxartnum = 2;
175 
176  if ((!aggressive && type == kProbableGenericTelevision) ||
177  pginfo->GetRecordingGroup() == "Deleted" ||
178  pginfo->GetRecordingGroup() == "LiveTV")
179  dolookup = false;
180  if (dolookup || aggressive)
181  {
182  ArtworkMap map = GetArtwork(pginfo->GetInetRef(), pginfo->GetSeason(), true);
183  if (map.isEmpty() || (aggressive && map.count() < maxartnum))
184  {
185  QString msg = QString("Looking up artwork for recording: %1 %2")
186  .arg(pginfo->GetTitle(), pginfo->GetSubtitle());
187  LOG(VB_GENERAL, LOG_INFO, msg);
188 
189  m_busyRecList.append(pginfo);
190  m_metadataFactory->Lookup(pginfo, true, true, aggressive);
191  continue;
192  }
193  }
194  delete pginfo;
195  }
196 
197 }
198 
200 {
201  QMap< QString, ProgramInfo* > recMap;
202  QMap< QString, uint32_t > inUseMap = ProgramInfo::QueryInUseMap();
203  QMap< QString, bool > isJobRunning = ProgramInfo::QueryJobsRunning(JOB_COMMFLAG);
204 
205  ProgramList progList;
206 
207  LoadFromRecorded( progList, false, inUseMap, isJobRunning, recMap, -1 );
208 
209  for (auto *pg : progList)
210  {
211  auto *pginfo = new ProgramInfo(*pg);
212  if (pginfo && pginfo->GetInetRef().isEmpty())
213  {
214  auto *rule = new RecordingRule();
215  rule->m_recordID = pginfo->GetRecordingRuleID();
216  rule->Load();
217  if (!rule->m_inetref.isEmpty())
218  {
219  QString msg = QString("%1").arg(pginfo->GetTitle());
220  if (!pginfo->GetSubtitle().isEmpty())
221  msg += QString(": %1").arg(pginfo->GetSubtitle());
222  msg += " has no inetref, but its recording rule does. Copying...";
223  LOG(VB_GENERAL, LOG_INFO, msg);
224  pginfo->SaveInetRef(rule->m_inetref);
225  }
226  delete rule;
227  }
228  delete pginfo;
229  }
230 }
231 
232 void LookerUpper::customEvent(QEvent *levent)
233 {
234  if (levent->type() == MetadataFactoryMultiResult::kEventType)
235  {
236  auto *mfmr = dynamic_cast<MetadataFactoryMultiResult*>(levent);
237 
238  if (!mfmr)
239  return;
240 
241  MetadataLookupList list = mfmr->m_results;
242 
243  if (list.count() > 1)
244  {
245  int yearindex = -1;
246  MetadataLookup *exactTitleMeta = nullptr;
247  QDate exactTitleDate;
248  float exactTitlePopularity = 0.0;
249  bool foundMatchWithArt = false;
250 
251  for (int p = 0; p != list.size(); ++p)
252  {
253  auto *pginfo = list[p]->GetData().value<ProgramInfo *>();
254 
255  if (pginfo && (QString::compare(pginfo->GetTitle(), list[p]->GetBaseTitle(), Qt::CaseInsensitive)) == 0)
256  {
257  bool hasArtwork = ((!(list[p]->GetArtwork(kArtworkFanart)).empty()) ||
258  (!(list[p]->GetArtwork(kArtworkCoverart)).empty()) ||
259  (!(list[p]->GetArtwork(kArtworkBanner)).empty()));
260 
261  // After the first exact match, prefer any more popular one.
262  // Most of the Movie database entries have Popularity fields.
263  // The TV series database generally has no Popularity values specified,
264  // so if none are found so far in the search, pick the most recently
265  // released entry with artwork. Also, if the first exact match had
266  // no artwork, prefer any later exact match with artwork.
267  if ((exactTitleMeta == nullptr) ||
268  (hasArtwork &&
269  ((!foundMatchWithArt) ||
270  ((list[p]->GetPopularity() > exactTitlePopularity)) ||
271  ((exactTitlePopularity == 0.0F) && (list[p]->GetReleaseDate() > exactTitleDate)))))
272  {
273  // remember the most popular or most recently released exact match
274  exactTitleDate = list[p]->GetReleaseDate();
275  exactTitlePopularity = list[p]->GetPopularity();
276  exactTitleMeta = list[p];
277  }
278  }
279 
280  if (pginfo && !pginfo->GetSeriesID().isEmpty() &&
281  pginfo->GetSeriesID() == (list[p])->GetTMSref())
282  {
283  MetadataLookup *lookup = list[p];
284  if (lookup->GetSubtype() != kProbableGenericTelevision)
285  pginfo->SaveSeasonEpisode(lookup->GetSeason(), lookup->GetEpisode());
286  pginfo->SaveInetRef(lookup->GetInetref());
287  m_busyRecList.removeAll(pginfo);
288  return;
289  }
290  if (pginfo && pginfo->GetYearOfInitialRelease() != 0 &&
291  (list[p])->GetYear() != 0 &&
292  pginfo->GetYearOfInitialRelease() == (list[p])->GetYear())
293  {
294  if (yearindex != -1)
295  {
296  LOG(VB_GENERAL, LOG_INFO, "Multiple results matched on year. No definite "
297  "match could be found.");
298  m_busyRecList.removeAll(pginfo);
299  return;
300  }
301  LOG(VB_GENERAL, LOG_INFO, "Matched from multiple results based on year. ");
302  yearindex = p;
303  }
304  }
305 
306  if (yearindex > -1)
307  {
308  MetadataLookup *lookup = list[yearindex];
309  auto *pginfo = lookup->GetData().value<ProgramInfo *>();
310  if (lookup->GetSubtype() != kProbableGenericTelevision)
311  pginfo->SaveSeasonEpisode(lookup->GetSeason(), lookup->GetEpisode());
312  pginfo->SaveInetRef(lookup->GetInetref());
313  m_busyRecList.removeAll(pginfo);
314  return;
315  }
316 
317  if (exactTitleMeta != nullptr)
318  {
319  LOG(VB_GENERAL, LOG_INFO, QString("Best match released %1").arg(exactTitleDate.toString()));
320  MetadataLookup *lookup = exactTitleMeta;
321  auto *pginfo = exactTitleMeta->GetData().value<ProgramInfo *>();
322  if (lookup->GetSubtype() != kProbableGenericTelevision)
323  pginfo->SaveSeasonEpisode(lookup->GetSeason(), lookup->GetEpisode());
324  pginfo->SaveInetRef(lookup->GetInetref());
325  m_busyRecList.removeAll(pginfo);
326  return;
327  }
328 
329  LOG(VB_GENERAL, LOG_INFO, "Unable to match this title, too many possible matches. "
330  "You may wish to manually set the season, episode, and "
331  "inetref in the 'Watch Recordings' screen.");
332 
333  auto *pginfo = list[0]->GetData().value<ProgramInfo *>();
334 
335  if (pginfo)
336  {
337  m_busyRecList.removeAll(pginfo);
338  }
339  }
340  }
341  else if (levent->type() == MetadataFactorySingleResult::kEventType)
342  {
343  auto *mfsr = dynamic_cast<MetadataFactorySingleResult*>(levent);
344 
345  if (!mfsr)
346  return;
347 
348  MetadataLookup *lookup = mfsr->m_result;
349 
350  if (!lookup)
351  return;
352 
353  auto *pginfo = lookup->GetData().value<ProgramInfo *>();
354 
355  // This null check could hang us as this pginfo would then never be
356  // removed
357  if (!pginfo)
358  return;
359 
360  LOG(VB_GENERAL, LOG_DEBUG, "I found the following data:");
361  LOG(VB_GENERAL, LOG_DEBUG,
362  QString(" Input Title: %1").arg(pginfo->GetTitle()));
363  LOG(VB_GENERAL, LOG_DEBUG,
364  QString(" Input Sub: %1").arg(pginfo->GetSubtitle()));
365  LOG(VB_GENERAL, LOG_DEBUG,
366  QString(" Title: %1").arg(lookup->GetTitle()));
367  LOG(VB_GENERAL, LOG_DEBUG,
368  QString(" Subtitle: %1").arg(lookup->GetSubtitle()));
369  LOG(VB_GENERAL, LOG_DEBUG,
370  QString(" Season: %1").arg(lookup->GetSeason()));
371  LOG(VB_GENERAL, LOG_DEBUG,
372  QString(" Episode: %1").arg(lookup->GetEpisode()));
373  LOG(VB_GENERAL, LOG_DEBUG,
374  QString(" Inetref: %1").arg(lookup->GetInetref()));
375  LOG(VB_GENERAL, LOG_DEBUG,
376  QString(" User Rating: %1").arg(lookup->GetUserRating()));
377 
378  if (lookup->GetSubtype() != kProbableGenericTelevision)
379  pginfo->SaveSeasonEpisode(lookup->GetSeason(), lookup->GetEpisode());
380  pginfo->SaveInetRef(lookup->GetInetref());
381 
382  if (m_updaterules)
383  {
384  auto *rule = new RecordingRule();
385  if (rule)
386  {
387  rule->LoadByProgram(pginfo);
388  if (rule->m_inetref.isEmpty() &&
389  (rule->m_searchType == kNoSearch))
390  {
391  rule->m_inetref = lookup->GetInetref();
392  }
393  rule->m_season = lookup->GetSeason();
394  rule->m_episode = lookup->GetEpisode();
395  rule->Save();
396 
397  delete rule;
398  }
399  }
400 
401  if (m_updateartwork)
402  {
403  DownloadMap dlmap = lookup->GetDownloads();
404  // Convert from QMap to QMultiMap
405  ArtworkMap artmap;
406  for (auto it = dlmap.cbegin(); it != dlmap.cend(); it++)
407  artmap.insert(it.key(), it.value());
408  SetArtwork(lookup->GetInetref(),
409  lookup->GetIsCollection() ? 0 : lookup->GetSeason(),
410  gCoreContext->GetMasterHostName(), artmap);
411  }
412 
413  m_busyRecList.removeAll(pginfo);
414  }
415  else if (levent->type() == MetadataFactoryNoResult::kEventType)
416  {
417  auto *mfnr = dynamic_cast<MetadataFactoryNoResult*>(levent);
418 
419  if (!mfnr)
420  return;
421 
422  MetadataLookup *lookup = mfnr->m_result;
423 
424  if (!lookup)
425  return;
426 
427  auto *pginfo = lookup->GetData().value<ProgramInfo *>();
428 
429  // This null check could hang us as this pginfo would then never be removed
430  if (!pginfo)
431  return;
432 
433  m_busyRecList.removeAll(pginfo);
434  }
435 }
MetadataFactory::Lookup
void Lookup(ProgramInfo *pginfo, bool automatic=true, bool getimages=true, bool allowgeneric=false)
Definition: metadatafactory.cpp:139
LookerUpper::HandleSingleRecording
void HandleSingleRecording(uint chanid, const QDateTime &starttime, bool updaterules=false)
Definition: lookup.cpp:36
MythCoreContext::GetMasterHostName
QString GetMasterHostName(void)
Definition: mythcorecontext.cpp:813
LookerUpper::StillWorking
bool StillWorking()
Definition: lookup.cpp:30
metadataimagehelper.h
LookerUpper::HandleAllRecordingRules
void HandleAllRecordingRules(void)
Definition: lookup.cpp:92
MetadataLookup::GetTitle
QString GetTitle() const
Definition: metadatacommon.h:299
LookerUpper::customEvent
void customEvent(QEvent *event) override
Definition: lookup.cpp:232
MetadataLookup::GetSubtype
LookupType GetSubtype() const
Definition: metadatacommon.h:287
kProbableMovie
@ kProbableMovie
Definition: metadatacommon.h:53
LookerUpper::HandleAllRecordings
void HandleAllRecordings(bool updaterules=false)
Definition: lookup.cpp:56
ProgramInfo::QueryJobsRunning
static QMap< QString, bool > QueryJobsRunning(int type)
Definition: programinfo.cpp:5591
GetArtwork
ArtworkMap GetArtwork(const QString &inetref, uint season, bool strict)
Definition: metadataimagehelper.cpp:10
RecordingRule
Internal representation of a recording rule, mirrors the record table.
Definition: recordingrule.h:28
GuessLookupType
LookupType GuessLookupType(ProgramInfo *pginfo)
Definition: metadatafactory.cpp:646
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
LoadFromRecorded
bool LoadFromRecorded(ProgramList &destination, bool possiblyInProgressRecordingsOnly, const QMap< QString, uint32_t > &inUseMap, const QMap< QString, bool > &isJobRunning, const QMap< QString, ProgramInfo * > &recMap, int sort, const QString &sortBy, bool ignoreLiveTV, bool ignoreDeleted)
Definition: programinfo.cpp:6130
remoteutil.h
MetadataFactoryNoResult
Definition: metadatafactory.h:50
MetadataLookup
Definition: metadatacommon.h:87
LookerUpper::m_updaterules
bool m_updaterules
Definition: lookup.h:35
MetadataFactoryNoResult::kEventType
static const Type kEventType
Definition: metadatafactory.h:65
MetadataLookup::GetDownloads
DownloadMap GetDownloads() const
Definition: metadatacommon.h:372
kArtworkCoverart
@ kArtworkCoverart
Definition: metadataimagehelper.h:11
programinfo.h
kArtworkFanart
@ kArtworkFanart
Definition: metadataimagehelper.h:12
mythlogging.h
MetadataLookup::GetEpisode
uint GetEpisode() const
Definition: metadatacommon.h:315
LookerUpper::LookerUpper
LookerUpper()
Definition: lookup.cpp:19
LookerUpper::m_updateartwork
bool m_updateartwork
Definition: lookup.h:36
hardwareprofile.config.p
p
Definition: config.py:33
RefCountedList< MetadataLookup >
MetadataLookup::GetData
QVariant GetData() const
Definition: metadatacommon.h:288
SetArtwork
bool SetArtwork(const QString &inetref, uint season, const QString &host, const QString &coverart, const QString &fanart, const QString &banner)
Definition: metadataimagehelper.cpp:82
kArtworkBanner
@ kArtworkBanner
Definition: metadataimagehelper.h:13
kNoSearch
@ kNoSearch
Definition: recordingtypes.h:79
MetadataLookup::GetSubtitle
QString GetSubtitle() const
Definition: metadatacommon.h:310
ProgramInfo::SaveSeasonEpisode
void SaveSeasonEpisode(uint seas, uint ep)
Definition: programinfo.cpp:5011
JOB_COMMFLAG
@ JOB_COMMFLAG
Definition: jobqueue.h:79
MetadataFactory::IsRunning
bool IsRunning()
Definition: metadatafactory.h:113
MetadataFactoryMultiResult::kEventType
static const Type kEventType
Definition: metadatafactory.h:29
LookerUpper::m_busyRecList
QList< ProgramInfo * > m_busyRecList
Definition: lookup.h:34
jobqueue.h
LookerUpper::~LookerUpper
~LookerUpper() override
Definition: lookup.cpp:24
LookerUpper::m_metadataFactory
MetadataFactory * m_metadataFactory
Definition: lookup.h:32
MetadataLookup::GetUserRating
float GetUserRating() const
Definition: metadatacommon.h:302
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
MetadataFactoryMultiResult
Definition: metadatafactory.h:20
MetadataFactory
Definition: metadatafactory.h:85
MetadataLookup::GetSeason
uint GetSeason() const
Definition: metadatacommon.h:314
AutoDeleteDeque< ProgramInfo * >
kProbableGenericTelevision
@ kProbableGenericTelevision
Definition: metadatacommon.h:52
MetadataFactorySingleResult
Definition: metadatafactory.h:32
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:67
MetadataFactorySingleResult::kEventType
static const Type kEventType
Definition: metadatafactory.h:47
LookupType
LookupType
Definition: metadatacommon.h:50
mythcorecontext.h
LookerUpper::CopyRuleInetrefsToRecordings
static void CopyRuleInetrefsToRecordings()
Definition: lookup.cpp:199
MetadataLookup::GetInetref
QString GetInetref() const
Definition: metadatacommon.h:356
RemoteGetAllScheduledRecordings
void RemoteGetAllScheduledRecordings(std::vector< ProgramInfo * > &scheduledlist)
Definition: remoteutil.cpp:165
ProgramInfo::QueryInUseMap
static QMap< QString, uint32_t > QueryInUseMap(void)
Definition: programinfo.cpp:5555
lookup.h
DownloadMap
QMap< VideoArtworkType, ArtworkInfo > DownloadMap
Definition: metadatacommon.h:84
ArtworkMap
QMultiMap< VideoArtworkType, ArtworkInfo > ArtworkMap
Definition: metadataimagehelper.h:31
MetadataLookup::GetIsCollection
bool GetIsCollection() const
Definition: metadatacommon.h:360
recordingrule.h
LookerUpper::HandleAllArtwork
void HandleAllArtwork(bool aggressive=false)
Definition: lookup.cpp:119
uint
unsigned int uint
Definition: freesurround.h:24