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