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