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").arg(pginfo->GetTitle())
77  .arg(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").arg(pginfo->GetTitle())
102  .arg(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())
140  .arg(pginfo->GetSubtitle());
141  LOG(VB_GENERAL, LOG_INFO, msg);
142 
143  m_busyRecList.append(pginfo);
144  m_metadataFactory->Lookup(pginfo, true, true, true);
145  continue;
146  }
147  }
148  delete pginfo;
149  }
150 
151  // Now, Attempt to fill in the gaps for recordings
152  QMap< QString, ProgramInfo* > recMap;
153  QMap< QString, uint32_t > inUseMap = ProgramInfo::QueryInUseMap();
154  QMap< QString, bool > isJobRunning = ProgramInfo::QueryJobsRunning(JOB_COMMFLAG);
155 
156  ProgramList progList;
157 
158  LoadFromRecorded( progList, false, inUseMap, isJobRunning, recMap, -1 );
159 
160  for (auto *pg : progList)
161  {
162  auto *pginfo = new ProgramInfo(*pg);
163 
164  bool dolookup = true;
165 
166  LookupType type = GuessLookupType(pginfo);
167 
168  if (type == kProbableMovie)
169  maxartnum = 2;
170 
171  if ((!aggressive && type == kProbableGenericTelevision) ||
172  pginfo->GetRecordingGroup() == "Deleted" ||
173  pginfo->GetRecordingGroup() == "LiveTV")
174  dolookup = false;
175  if (dolookup || aggressive)
176  {
177  ArtworkMap map = GetArtwork(pginfo->GetInetRef(), pginfo->GetSeason(), true);
178  if (map.isEmpty() || (aggressive && map.count() < maxartnum))
179  {
180  QString msg = QString("Looking up artwork for recording: %1 %2")
181  .arg(pginfo->GetTitle())
182  .arg(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  ArtworkMap map = lookup->GetDownloads();
400  SetArtwork(lookup->GetInetref(),
401  lookup->GetIsCollection() ? 0 : lookup->GetSeason(),
403  }
404 
405  m_busyRecList.removeAll(pginfo);
406  }
407  else if (levent->type() == MetadataFactoryNoResult::kEventType)
408  {
409  auto *mfnr = dynamic_cast<MetadataFactoryNoResult*>(levent);
410 
411  if (!mfnr)
412  return;
413 
414  MetadataLookup *lookup = mfnr->m_result;
415 
416  if (!lookup)
417  return;
418 
419  auto *pginfo = lookup->GetData().value<ProgramInfo *>();
420 
421  // This null check could hang us as this pginfo would then never be removed
422  if (!pginfo)
423  return;
424 
425  m_busyRecList.removeAll(pginfo);
426  }
427 }
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:828
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:298
LookerUpper::customEvent
void customEvent(QEvent *event) override
Definition: lookup.cpp:228
MetadataLookup::GetSubtype
LookupType GetSubtype() const
Definition: metadatacommon.h:286
LookerUpper::HandleAllRecordings
void HandleAllRecordings(bool updaterules=false)
Definition: lookup.cpp:54
JOB_COMMFLAG
@ JOB_COMMFLAG
Definition: jobqueue.h:73
ProgramInfo::QueryJobsRunning
static QMap< QString, bool > QueryJobsRunning(int type)
Definition: programinfo.cpp:5302
arg
arg(title).arg(filename).arg(doDelete))
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:33
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:51
MetadataLookup
Definition: metadatacommon.h:87
LookerUpper::m_updaterules
bool m_updaterules
Definition: lookup.h:35
MetadataLookup::GetDownloads
DownloadMap GetDownloads() const
Definition: metadatacommon.h:371
programinfo.h
mythlogging.h
MetadataLookup::GetEpisode
uint GetEpisode() const
Definition: metadatacommon.h:314
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:287
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:309
ProgramInfo::SaveSeasonEpisode
void SaveSeasonEpisode(uint seas, uint ep)
Definition: programinfo.cpp:4725
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:141
MetadataLookup::GetUserRating
float GetUserRating() const
Definition: metadatacommon.h:301
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
kProbableGenericTelevision
@ kProbableGenericTelevision
Definition: metadatacommon.h:51
MetadataFactoryMultiResult
Definition: metadatafactory.h:21
MetadataFactory
Definition: metadatafactory.h:86
MetadataLookup::GetSeason
uint GetSeason() const
Definition: metadatacommon.h:313
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:5817
AutoDeleteDeque< ProgramInfo * >
MetadataFactorySingleResult
Definition: metadatafactory.h:33
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:68
mythcorecontext.h
LookerUpper::CopyRuleInetrefsToRecordings
static void CopyRuleInetrefsToRecordings()
Definition: lookup.cpp:195
MetadataLookup::GetInetref
QString GetInetref() const
Definition: metadatacommon.h:355
ProgramInfo::QueryInUseMap
static QMap< QString, uint32_t > QueryInUseMap(void)
Definition: programinfo.cpp:5266
LookupType
LookupType
Definition: metadatacommon.h:49
kArtworkBanner
@ kArtworkBanner
Definition: metadataimagehelper.h:13
lookup.h
ArtworkMap
QMultiMap< VideoArtworkType, ArtworkInfo > ArtworkMap
Definition: metadataimagehelper.h:31
MetadataLookup::GetIsCollection
bool GetIsCollection() const
Definition: metadatacommon.h:359
recordingrule.h
kProbableMovie
@ kProbableMovie
Definition: metadatacommon.h:52
kArtworkCoverart
@ kArtworkCoverart
Definition: metadataimagehelper.h:11
LookerUpper::HandleAllArtwork
void HandleAllArtwork(bool aggressive=false)
Definition: lookup.cpp:113