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