33#define LOC QString("FillData: ")
34#define LOC_WARN QString("FillData, Warning: ")
35#define LOC_ERR QString("FillData, Error: ")
90 QMap<QString, QList<ProgInfo> > proglist;
98 if (proglist.count() != 0)
100 LOG(VB_GENERAL, LOG_INFO,
"Skipping program guide updates");
105 if (proglist.count() == 0)
107 LOG(VB_GENERAL, LOG_INFO,
"No programs found in data.");
122 const QString templatename =
"/tmp/mythXXXXXX";
124 if (templatename == tempfilename)
130 QString
filename = QString(tempfilename);
135 query1.
prepare(
"SELECT configpath FROM videosource"
136 " WHERE sourceid = :ID AND configpath IS NOT NULL");
145 configfile = query1.
value(0).toString();
147 configfile = QString(
"%1/%2.xmltv").arg(
GetConfDir(), source.
name);
149 LOG(VB_GENERAL, LOG_INFO,
150 QString(
"XMLTV config file is: %1").arg(configfile));
152 QString command = QString(
"nice %1 --config-file '%2' --output %3")
153 .arg(xmltv_grabber, configfile,
filename);
157 command +=
" --list-channels";
165 command += QString(
" --days 1 --offset %1").arg(offset);
169 command +=
" --quiet";
176 LOG(VB_XMLTV, LOG_INFO,
180 QString status = QObject::tr(
"currently running.");
185 LOG(VB_XMLTV, LOG_INFO, QString(
"Grabber Command: %1").arg(command));
187 LOG(VB_XMLTV, LOG_INFO,
188 "----------------- Start of XMLTV output -----------------");
193 uint systemcall_status = run_grabber.
Wait();
197 QTextStream ostream(result);
198 while (!ostream.atEnd())
200 QString line = ostream.readLine().simplified();
201 LOG(VB_XMLTV, LOG_INFO, line);
204 LOG(VB_XMLTV, LOG_INFO,
205 "------------------ End of XMLTV output ------------------");
209 status = QObject::tr(
"Successful.");
216 status = QObject::tr(
"FAILED: XMLTV grabber ran but was interrupted.");
217 LOG(VB_GENERAL, LOG_ERR,
218 QString(
"XMLTV grabber ran but was interrupted."));
222 status = QObject::tr(
"FAILED: XMLTV grabber returned error code %1.")
223 .arg(systemcall_status);
224 LOG(VB_GENERAL, LOG_ERR,
225 QString(
"XMLTV grabber returned error code %1")
226 .arg(systemcall_status));
247 DataSourceList::iterator it;
252 QDateTime GuideDataBefore;
253 QDateTime GuideDataAfter;
255 int externally_handled = 0;
256 int total_sources = sourcelist.size();
257 int source_channels = 0;
259 QString sidStr = QString(
"Updating source #%1 (%2) with grabber %3");
264 for (it = sourcelist.begin(); it != sourcelist.end(); ++it)
269 QString xmltv_grabber = (*it).xmltvgrabber;
271 if (xmltv_grabber ==
"datadirect" ||
272 xmltv_grabber ==
"schedulesdirect1")
274 LOG(VB_GENERAL, LOG_ERR,
275 QString(
"Source %1 is configured to use the DataDirect guide"
276 "service from Schedules Direct. That service is no "
277 "longer supported by MythTV. Update to use one of "
278 "the XMLTV grabbers that use the JSON-based guide "
279 "service from Schedules Direct.")
284 query.
prepare(
"SELECT MAX(endtime) "
286 "LEFT JOIN channel c ON p.chanid=c.chanid "
287 "WHERE c.deleted IS NULL AND c.sourceid= :SRCID "
288 " AND manualid = 0 AND c.xmltvid != '';");
301 if (xmltv_grabber ==
"eitonly")
303 LOG(VB_GENERAL, LOG_INFO,
304 QString(
"Source %1 configured to use only the "
305 "broadcasted guide data. Skipping.") .arg((*it).id));
307 externally_handled++;
312 if (xmltv_grabber.trimmed().isEmpty() ||
313 xmltv_grabber ==
"/bin/true" ||
314 xmltv_grabber ==
"none")
316 LOG(VB_GENERAL, LOG_INFO,
317 QString(
"Source %1 configured with no grabber. Nothing to do.")
320 externally_handled++;
326 LOG(VB_GENERAL, LOG_INFO, sidStr.arg(QString::number((*it).id),
331 "SELECT COUNT(chanid) FROM channel "
332 "WHERE deleted IS NULL AND "
333 " sourceid = :SRCID AND xmltvid != ''");
338 source_channels = query.
value(0).toInt();
339 if (source_channels > 0)
341 LOG(VB_GENERAL, LOG_INFO,
342 QString(
"Found %1 channels for source %2 which use grabber")
343 .arg(source_channels).arg((*it).id));
347 LOG(VB_GENERAL, LOG_INFO,
348 QString(
"No channels are configured to use grabber (none have XMLTVIDs)."));
354 LOG(VB_GENERAL, LOG_INFO,
355 QString(
"Can't get a channel count for source id %1")
359 bool hasprefmethod =
false;
365 QStringList(
"--capabilities"),
367 grabber_capabilities_proc.
Run(25s);
370 LOG(VB_GENERAL, LOG_ERR,
371 QString(
"%1 --capabilities failed or we timed out waiting."
372 " You may need to upgrade your xmltv grabber")
373 .arg(xmltv_grabber));
377 QByteArray result = grabber_capabilities_proc.
ReadAll();
378 QTextStream ostream(result);
379 QString capabilities;
380 while (!ostream.atEnd())
383 = ostream.readLine().simplified();
385 if (capability.isEmpty())
388 capabilities += capability +
' ';
390 if (capability ==
"baseline")
391 (*it).xmltvgrabber_baseline =
true;
393 if (capability ==
"manualconfig")
394 (*it).xmltvgrabber_manualconfig =
true;
396 if (capability ==
"cache")
397 (*it).xmltvgrabber_cache =
true;
399 if (capability ==
"apiconfig")
400 (*it).xmltvgrabber_apiconfig =
true;
402 if (capability ==
"lineups")
403 (*it).xmltvgrabber_lineups =
true;
405 if (capability ==
"preferredmethod")
406 hasprefmethod =
true;
408 LOG(VB_GENERAL, LOG_INFO,
409 QString(
"Grabber has capabilities: %1") .arg(capabilities));
417 QStringList(
"--preferredmethod"),
419 grabber_method_proc.
Run(15s);
422 LOG(VB_GENERAL, LOG_ERR,
423 QString(
"%1 --preferredmethod failed or we timed out "
424 "waiting. You may need to upgrade your xmltv "
425 "grabber").arg(xmltv_grabber));
429 QTextStream ostream(grabber_method_proc.
ReadAll());
430 (*it).xmltvgrabber_prefmethod =
431 ostream.readLine().simplified();
433 LOG(VB_GENERAL, LOG_INFO, QString(
"Grabber prefers method: %1")
434 .arg((*it).xmltvgrabber_prefmethod));
445 else if ((*it).xmltvgrabber_baseline)
457 std::vector<bool> refresh_request;
464 for (
int i = 0; i < grabdays; i++)
468 for (
int i = 0; i < grabdays; i++)
479 i += (newDate.daysTo(qCurrentDate));
481 qCurrentDate = newDate;
484 QString currDate(qCurrentDate.addDays(i).toString());
486 LOG(VB_GENERAL, LOG_INFO,
"");
487 LOG(VB_GENERAL, LOG_INFO,
"Checking day @ " +
488 QString(
"offset %1, date: %2").arg(i).arg(currDate));
490 bool download_needed =
false;
492 if (refresh_request[i])
496 LOG(VB_GENERAL, LOG_INFO,
497 "Data Refresh always needed for tomorrow");
501 LOG(VB_GENERAL, LOG_INFO,
502 "Data Refresh needed because of user request");
504 download_needed =
true;
510 querystr =
"SELECT c.chanid, COUNT(p.starttime) "
512 "LEFT JOIN program p ON c.chanid = p.chanid "
514 "DATE_ADD(DATE_ADD(CURRENT_DATE(), "
515 "INTERVAL '%1' DAY), INTERVAL '20' HOUR) "
516 " AND starttime < DATE_ADD(CURRENT_DATE(), "
517 "INTERVAL '%2' DAY) "
518 "WHERE c.deleted IS NULL AND c.sourceid = %3 AND c.xmltvid != '' "
519 "GROUP BY c.chanid;";
521 if (query.
exec(querystr.arg(i-1).arg(i).arg((*it).id)) &&
524 int prevChanCount = 0;
525 int currentChanCount = 0;
526 int previousDayCount = 0;
527 int currentDayCount = 0;
529 LOG(VB_CHANNEL, LOG_INFO,
530 QString(
"Checking program counts for day %1")
535 if (query.
value(1).toInt() > 0)
537 previousDayCount += query.
value(1).toInt();
539 LOG(VB_CHANNEL, LOG_INFO,
540 QString(
" chanid %1 -> %2 programs")
541 .arg(query.
value(0).toString())
542 .arg(query.
value(1).toInt()));
545 if (query.
exec(querystr.arg(i).arg(i+1).arg((*it).id))
548 LOG(VB_CHANNEL, LOG_INFO,
549 QString(
"Checking program counts for day %1")
553 if (query.
value(1).toInt() > 0)
555 currentDayCount += query.
value(1).toInt();
557 LOG(VB_CHANNEL, LOG_INFO,
558 QString(
" chanid %1 -> %2 programs")
559 .arg(query.
value(0).toString())
560 .arg(query.
value(1).toInt()));
565 LOG(VB_GENERAL, LOG_INFO,
566 QString(
"Data Refresh because we are unable to "
567 "query the data for day %1 to "
568 "determine if we have enough").arg(i));
569 download_needed =
true;
572 if (currentChanCount < (prevChanCount * 0.90))
574 LOG(VB_GENERAL, LOG_INFO,
575 QString(
"Data refresh needed because only %1 "
576 "out of %2 channels have at least one "
577 "program listed for day @ offset %3 "
578 "from 8PM - midnight. Previous day "
579 "had %4 channels with data in that "
581 .arg(currentChanCount).arg(source_channels)
582 .arg(i).arg(prevChanCount));
583 download_needed =
true;
585 else if (currentDayCount == 0)
587 LOG(VB_GENERAL, LOG_INFO,
588 QString(
"Data refresh needed because no data "
589 "exists for day @ offset %1 from 8PM - "
590 "midnight.").arg(i));
591 download_needed =
true;
593 else if (previousDayCount == 0)
595 LOG(VB_GENERAL, LOG_INFO,
596 QString(
"Data refresh needed because no data "
597 "exists for day @ offset %1 from 8PM - "
598 "midnight. Unable to calculate how "
599 "much we should have for the current "
600 "day so a refresh is being forced.")
602 download_needed =
true;
604 else if (currentDayCount < (currentChanCount * 3))
606 LOG(VB_GENERAL, LOG_INFO,
607 QString(
"Data Refresh needed because offset "
608 "day %1 has less than 3 programs "
609 "per channel for the 8PM - midnight "
610 "time window for channels that "
611 "normally have data. "
612 "We want at least %2 programs, but "
614 .arg(i).arg(currentChanCount * 3)
615 .arg(currentDayCount));
616 download_needed =
true;
618 else if (currentDayCount < (previousDayCount / 2))
620 LOG(VB_GENERAL, LOG_INFO,
621 QString(
"Data Refresh needed because offset "
622 "day %1 has less than half the number "
623 "of programs as the previous day for "
624 "the 8PM - midnight time window. "
625 "We want at least %2 programs, but "
626 "only found %3").arg(i)
627 .arg(previousDayCount / 2)
628 .arg(currentDayCount));
629 download_needed =
true;
634 LOG(VB_GENERAL, LOG_INFO,
635 QString(
"Data Refresh needed because we are unable "
636 "to query the data for day @ offset %1 to "
637 "determine how much we should have for "
638 "offset day %2.").arg(i-1).arg(i));
639 download_needed =
true;
645 LOG(VB_GENERAL, LOG_NOTICE,
646 QString(
"Refreshing data for ") + currDate);
658 LOG(VB_GENERAL, LOG_INFO,
659 "Grabber is no longer returning program data, "
666 LOG(VB_GENERAL, LOG_NOTICE,
667 QString(
"Data is already present for ") + currDate +
676 LOG(VB_GENERAL, LOG_ERR,
677 QString(
"Grabbing XMLTV data using ") + xmltv_grabber +
678 " is not supported. You may need to upgrade to"
679 " the latest version of XMLTV.");
687 query.
prepare(
"SELECT MAX(endtime) FROM program p "
688 "LEFT JOIN channel c ON p.chanid=c.chanid "
689 "WHERE c.deleted IS NULL AND c.sourceid= :SRCID "
690 "AND manualid = 0 AND c.xmltvid != '';");
699 if (GuideDataAfter == GuideDataBefore)
709 LOG(VB_GENERAL, LOG_CRIT,
LOC +
"Encountered Fatal Error: " +
error);
720 (total_sources != externally_handled))
722 status = QObject::tr(
723 "mythfilldatabase ran, but did not insert "
724 "any new data into the Guide for %1 of %2 sources. "
725 "This can indicate a potential grabber failure.")
731 status = QObject::tr(
"Successful.");
737 return (failures == 0);
std::vector< ChannelInfo > ChannelInfoList
void handleChannels(int id, ChannelInfoList *chanlist) const
QMap< uint, bool > m_refreshDay
bool GrabData(const DataSource &source, int offset)
void SetRefresh(int day, bool set)
QStringList m_fatalErrors
bool m_onlyUpdateChannels
bool GrabDataFromFile(int id, const QString &filename)
bool Run(DataSourceList &sourcelist)
Goes through the sourcelist and updates its channels with program info grabbed with the associated gr...
XMLTVParser m_xmltvParser
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
bool isNull(int field) const
QVariant value(int i) const
bool isActive(void) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
static void DBError(const QString &where, const MSqlQuery &query)
uint Wait(std::chrono::seconds timeout=0s)
QByteArray & ReadAllErr()
void Run(std::chrono::seconds timeout=0s)
Runs a command inside the /bin/sh shell. Returns immediately.
static void HandlePrograms(uint sourceid, QMap< QString, QList< ProgInfo > > &proglist)
Called from mythfilldatabase to bulk insert data into the program database.
bool parseFile(const QString &filename, ChannelInfoList *chanlist, QMap< QString, QList< ProgInfo > > *proglist)
@ GENERIC_EXIT_OK
Exited with no error.
@ GENERIC_EXIT_KILLED
Process killed or stopped.
bool updateNextScheduledRun()
bool updateLastRunStatus(QString &status)
bool updateLastRunEnd(void)
bool updateLastRunStart(void)
static constexpr int8_t REFRESH_MAX
std::vector< DataSource > DataSourceList
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QString createTempFile(QString name_template, bool dir)
@ kMSStdErr
allow access to stderr
@ kMSStdOut
allow access to stdout
@ kMSRunShell
run process through shell
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
bool xmltvgrabber_apiconfig
QString xmltvgrabber_prefmethod
static bool is_grabber_external(const QString &grabber)