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