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