MythTV  master
programdata.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 // C++ includes
4 #include <algorithm>
5 #include <climits>
6 #include <utility>
7 
8 // Qt includes
9 #include <QtCore> // for qAbs
10 
11 // MythTV headers
12 #include "libmythbase/mythdb.h"
14 
15 #include "channelutil.h"
16 #include "mpeg/dvbdescriptors.h"
17 #include "programdata.h"
18 
19 #define LOC QString("ProgramData: ")
20 
21 static const std::array<const std::string,DBPerson::kGuest+1> roles
22 {
23  "",
24  "actor", "director", "producer", "executive_producer",
25  "writer", "guest_star", "host", "adapter",
26  "presenter", "commentator", "guest",
27 };
28 
29 static QString denullify(const QString &str)
30 {
31  return str.isNull() ? "" : str;
32 }
33 
34 static QVariant denullify(const QDateTime &dt)
35 {
36  return dt.isNull() ? QVariant("0000-00-00 00:00:00") : QVariant(dt);
37 }
38 
39 static void add_genres(MSqlQuery &query, const QStringList &genres,
40  uint chanid, const QDateTime &starttime)
41 {
42  QString relevance = QString("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
43  for (auto it = genres.constBegin(); (it != genres.constEnd()) &&
44  ((it - genres.constBegin()) < relevance.size()); ++it)
45  {
46  query.prepare(
47  "INSERT INTO programgenres "
48  " ( chanid, starttime, genre, relevance) "
49  "VALUES (:CHANID, :START, :genre, :relevance)");
50  query.bindValue(":CHANID", chanid);
51  query.bindValue(":START", starttime);
52  query.bindValue(":genre", *it);
53  query.bindValue(":relevance", relevance.at(it - genres.constBegin()));
54 
55  if (!query.exec())
56  MythDB::DBError("programgenres insert", query);
57  }
58 }
59 
61  : m_role(other.m_role)
62  , m_name(other.m_name)
63  , m_priority(other.m_priority)
64  , m_character(other.m_character)
65 {
66  m_name.squeeze();
67  m_character.squeeze();
68 }
69 
71 {
72  if (this == &rhs)
73  return *this;
74  m_role = rhs.m_role;
75  m_name = rhs.m_name;
76  m_name.squeeze();
77  m_priority = rhs.m_priority;
79  m_character.squeeze();
80  return *this;
81 }
82 
83 DBPerson::DBPerson(Role role, QString name, int priority,
84  QString character)
85  : m_role(role)
86  , m_name(std::move(name))
87  , m_priority(priority)
88  , m_character(std::move(character))
89 {
90  m_name.squeeze();
91  m_character.squeeze();
92 }
93 
94 DBPerson::DBPerson(const QString &role, QString name,
95  int priority, QString character)
96  : m_role(kUnknown)
97  , m_name(std::move(name))
98  , m_priority(priority)
99  , m_character(std::move(character))
100 {
101  if (!role.isEmpty())
102  {
103  std::string rolestr = role.toLower().toStdString();
104  for (size_t i = 0; i < roles.size(); ++i)
105  {
106  if (rolestr == roles[i])
107  m_role = (Role) i;
108  }
109  }
110  m_name.squeeze();
111  m_character.squeeze();
112 }
113 
114 QString DBPerson::GetRole(void) const
115 {
116  if ((m_role < kActor) || (m_role > kGuest))
117  return "guest";
118  return QString::fromStdString(roles[m_role]);
119 }
120 
121 QString DBPerson::toString(void) const
122 {
123  return QString("%1 %2 as %3").arg(m_role).arg(m_name, m_character);
124 }
125 
127  const QDateTime &starttime,
128  bool recording) const
129 {
130  uint personid = GetPersonDB(query);
131  if (!personid && InsertPersonDB(query))
132  personid = GetPersonDB(query);
133 
134  uint roleid = 0;
135  if (!m_character.isEmpty())
136  {
137  roleid = GetRoleDB(query);
138  if (!roleid && InsertRoleDB(query))
139  roleid = GetRoleDB(query);
140  }
141 
142  return InsertCreditsDB(query, personid, roleid, chanid,
143  starttime, recording);
144 }
145 
147 {
148  query.prepare(
149  "SELECT person "
150  "FROM people "
151  "WHERE name = :NAME");
152  query.bindValue(":NAME", m_name);
153 
154  if (!query.exec())
155  MythDB::DBError("get_person", query);
156  else if (query.next())
157  return query.value(0).toUInt();
158 
159  return 0;
160 }
161 
163 {
164  query.prepare(
165  "INSERT IGNORE INTO people (name) "
166  "VALUES (:NAME);");
167  query.bindValue(":NAME", m_name);
168 
169  if (query.exec())
170  return 1;
171 
172  MythDB::DBError("insert_person", query);
173  return 0;
174 }
175 
177 {
178  query.prepare(
179  "SELECT roleid "
180  "FROM roles "
181  "WHERE name = :NAME");
182  query.bindValue(":NAME", m_character);
183 
184  if (query.exec() && query.next())
185  return query.value(0).toUInt();
186 
187  return 0;
188 }
189 
191 {
192  query.prepare(
193  "INSERT IGNORE INTO roles (name) "
194  "VALUES (:NAME);");
195  query.bindValue(":NAME", m_character);
196 
197  if (query.exec())
198  return true;
199 
200  MythDB::DBError("insert_role", query);
201  return false;
202 }
203 
205  uint chanid, const QDateTime &starttime,
206  bool recording) const
207 {
208  if (!personid)
209  return 0;
210 
211  QString table = recording ? "recordedcredits" : "credits";
212 
213  query.prepare(QString("REPLACE INTO %1 "
214  " ( person, roleid, chanid, starttime, role, priority) "
215  "VALUES (:PERSON, :ROLEID, :CHANID, :STARTTIME, :ROLE, :PRIORITY);")
216  .arg(table));
217  query.bindValue(":PERSON", personid);
218  query.bindValue(":ROLEID", roleid);
219  query.bindValue(":CHANID", chanid);
220  query.bindValue(":STARTTIME", starttime);
221  query.bindValue(":ROLE", GetRole());
222  query.bindValue(":PRIORITY", m_priority);
223 
224  if (query.exec())
225  return 1;
226 
227  MythDB::DBError("insert_credits", query);
228  return 0;
229 }
230 
232 {
233  if (this == &other)
234  return *this;
235 
236  m_title = other.m_title;
237  m_subtitle = other.m_subtitle;
239  m_category = other.m_category;
240  m_starttime = other.m_starttime;
241  m_endtime = other.m_endtime;
242  m_airdate = other.m_airdate;
244 
245  if (m_credits != other.m_credits)
246  {
247  if (m_credits)
248  {
249  delete m_credits;
250  m_credits = nullptr;
251  }
252 
253  if (other.m_credits)
254  {
255  m_credits = new DBCredits;
256  m_credits->insert(m_credits->end(),
257  other.m_credits->begin(),
258  other.m_credits->end());
259  }
260  }
261 
262  m_partnumber = other.m_partnumber;
263  m_parttotal = other.m_parttotal;
266  m_audioProps = other.m_audioProps;
267  m_videoProps = other.m_videoProps;
268  m_stars = other.m_stars;
270  m_seriesId = other.m_seriesId;
271  m_programId = other.m_programId;
272  m_inetref = other.m_inetref;
274  m_ratings = other.m_ratings;
276  m_season = other.m_season;
277  m_episode = other.m_episode;
279  m_genres = other.m_genres;
280 
281  Squeeze();
282 
283  return *this;
284 }
285 
287 {
288  m_title.squeeze();
289  m_subtitle.squeeze();
290  m_description.squeeze();
291  m_category.squeeze();
292  m_syndicatedepisodenumber.squeeze();
293  m_seriesId.squeeze();
294  m_programId.squeeze();
295  m_inetref.squeeze();
296 }
297 
298 void DBEvent::AddPerson(DBPerson::Role role, const QString &name,
299  int priority, const QString &character)
300 {
301  if (!m_credits)
302  m_credits = new DBCredits;
303 
304  m_credits->push_back(DBPerson(role, name.simplified(),
305  priority, character.simplified()));
306 }
307 
308 void DBEvent::AddPerson(const QString &role, const QString &name,
309  int priority, const QString &character)
310 {
311  if (!m_credits)
312  m_credits = new DBCredits;
313 
314  m_credits->push_back(DBPerson(role, name.simplified(),
315  priority, character.simplified()));
316 }
317 
318 bool DBEvent::HasTimeConflict(const DBEvent &o) const
319 {
320  return ((m_starttime <= o.m_starttime && o.m_starttime < m_endtime) ||
321  (o.m_endtime <= m_endtime && m_starttime < o.m_endtime));
322 }
323 
324 // Processing new EIT entry starts here
326  MSqlQuery &query, uint chanid, int match_threshold) const
327 {
328  // List the program that we are going to add
329  LOG(VB_EIT, LOG_DEBUG,
330  QString("EIT: new program: %1 %2 '%3' chanid %4")
331  .arg(m_starttime.toString(Qt::ISODate),
332  m_endtime.toString(Qt::ISODate),
333  m_title.left(35),
334  QString::number(chanid)));
335 
336  // Do not insert or update when the program is in the past
337  QDateTime now = QDateTime::currentDateTimeUtc();
338  if (m_endtime < now)
339  {
340  LOG(VB_EIT, LOG_DEBUG,
341  QString("EIT: skip '%1' endtime is in the past")
342  .arg(m_title.left(35)));
343  return 0;
344  }
345 
346  // Get all programs already in the database that overlap
347  // with our new program.
348  std::vector<DBEvent> programs;
349  uint count = GetOverlappingPrograms(query, chanid, programs);
350  int match = INT_MIN;
351  int i = -1;
352 
353  // If there are no programs already in the database that overlap
354  // with our new program then we can simply insert it in the database.
355  if (!count)
356  return InsertDB(query, chanid);
357 
358  // List all overlapping programs with start- and endtime.
359  for (uint j=0; j<count; ++j)
360  {
361  LOG(VB_EIT, LOG_DEBUG,
362  QString("EIT: overlap[%1] : %2 %3 '%4'")
363  .arg(QString::number(j),
364  programs[j].m_starttime.toString(Qt::ISODate),
365  programs[j].m_endtime.toString(Qt::ISODate),
366  programs[j].m_title.left(35)));
367  }
368 
369  // Determine which of the overlapping programs is a match with
370  // our new program; if we have a match then our new program is considered
371  // to be an update of the matching program.
372  // The 2nd parameter "i" is the index of the best matching program.
373  match = GetMatch(programs, i);
374 
375  // Update an existing program or insert a new program.
376  if (match >= match_threshold)
377  {
378  // We have a good match; update program[i] in the database
379  // with the new program data and move the overlapping programs
380  // out of the way.
381  LOG(VB_EIT, LOG_DEBUG,
382  QString("EIT: accept match[%1]: %2 '%3' vs. '%4'")
383  .arg(i).arg(match)
384  .arg(m_title.left(35),
385  programs[i].m_title.left(35)));
386  return UpdateDB(query, chanid, programs, i);
387  }
388 
389  // If we are here then either we have a match but the match is
390  // not good enough (the "i >= 0" case) or we did not find
391  // a match at all.
392  if (i >= 0)
393  {
394  LOG(VB_EIT, LOG_DEBUG,
395  QString("EIT: reject match[%1]: %2 '%3' vs. '%4'")
396  .arg(i).arg(match)
397  .arg(m_title.left(35),
398  programs[i].m_title.left(35)));
399  }
400 
401  // Move the overlapping programs out of the way and
402  // insert the new program.
403  return UpdateDB(query, chanid, programs, -1);
404 }
405 
406 // Get all programs in the database that overlap with our new program.
407 // We check for three ways in which we can have an overlap:
408 // (1) Start of old program is inside our new program:
409 // old program starts at or after our program AND
410 // old program starts before end of our program;
411 // e.g. new program s-------------e
412 // old program s-------------e
413 // or old program s-----e
414 // This is the STIME1/ETIME1 comparison.
415 // (2) End of old program is inside our new program:
416 // old program ends after our program starts AND
417 // old program ends before end of our program
418 // e.g. new program s-------------e
419 // old program s-------------e
420 // or old program s-----e
421 // This is the STIME2/ETIME2 comparison.
422 // (3) We can have a new program is "inside" the old program:
423 // old program starts before our program AND
424 // old program ends after end of our program
425 // e.g. new program s---------e
426 // old program s-----------------e
427 // This is the STIME3/ETIME3 comparison.
428 //
430  MSqlQuery &query, uint chanid, std::vector<DBEvent> &programs) const
431 {
432  uint count = 0;
433  query.prepare(
434  "SELECT title, subtitle, description, "
435  " category, category_type, "
436  " starttime, endtime, "
437  " subtitletypes+0,audioprop+0, videoprop+0, "
438  " seriesid, programid, "
439  " partnumber, parttotal, "
440  " syndicatedepisodenumber, "
441  " airdate, originalairdate, "
442  " previouslyshown,listingsource, "
443  " stars+0, "
444  " season, episode, totalepisodes, "
445  " inetref "
446  "FROM program "
447  "WHERE chanid = :CHANID AND "
448  " manualid = 0 AND "
449  " ( ( starttime >= :STIME1 AND starttime < :ETIME1 ) OR "
450  " ( endtime > :STIME2 AND endtime <= :ETIME2 ) OR "
451  " ( starttime < :STIME3 AND endtime > :ETIME3 ) )");
452  query.bindValue(":CHANID", chanid);
453  query.bindValue(":STIME1", m_starttime);
454  query.bindValue(":ETIME1", m_endtime);
455  query.bindValue(":STIME2", m_starttime);
456  query.bindValue(":ETIME2", m_endtime);
457  query.bindValue(":STIME3", m_starttime);
458  query.bindValue(":ETIME3", m_endtime);
459 
460  if (!query.exec())
461  {
462  MythDB::DBError("GetOverlappingPrograms 1", query);
463  return 0;
464  }
465 
466  while (query.next())
467  {
468  ProgramInfo::CategoryType category_type =
469  string_to_myth_category_type(query.value(4).toString());
470 
471  DBEvent prog(
472  query.value(0).toString(),
473  query.value(1).toString(),
474  query.value(2).toString(),
475  query.value(3).toString(),
476  category_type,
477  MythDate::as_utc(query.value(5).toDateTime()),
478  MythDate::as_utc(query.value(6).toDateTime()),
479  query.value(7).toUInt(),
480  query.value(8).toUInt(),
481  query.value(9).toUInt(),
482  query.value(19).toDouble(),
483  query.value(10).toString(),
484  query.value(11).toString(),
485  query.value(18).toUInt(),
486  query.value(20).toUInt(), // Season
487  query.value(21).toUInt(), // Episode
488  query.value(22).toUInt()); // Total Episodes
489 
490  prog.m_inetref = query.value(23).toString();
491  prog.m_partnumber = query.value(12).toUInt();
492  prog.m_parttotal = query.value(13).toUInt();
493  prog.m_syndicatedepisodenumber = query.value(14).toString();
494  prog.m_airdate = query.value(15).toUInt();
495  prog.m_originalairdate = query.value(16).toDate();
496  prog.m_previouslyshown = query.value(17).toBool();
497 
498  programs.push_back(prog);
499  count++;
500  }
501 
502  return count;
503 }
504 
505 
506 static int score_words(const QStringList &al, const QStringList &bl)
507 {
508  QStringList::const_iterator ait = al.begin();
509  QStringList::const_iterator bit = bl.begin();
510  int score = 0;
511  for (; (ait != al.end()) && (bit != bl.end()); ++ait)
512  {
513  QStringList::const_iterator bit2 = bit;
514  int dist = 0;
515  int bscore = 0;
516  for (; bit2 != bl.end(); ++bit2)
517  {
518  if (*ait == *bit)
519  {
520  bscore = std::max(1000, 2000 - (dist * 500));
521  // lower score for short words
522  if (ait->length() < 5)
523  bscore /= 5 - ait->length();
524  break;
525  }
526  dist++;
527  }
528  if (bscore && dist < 3)
529  {
530  for (int i = 0; (i < dist) && bit != bl.end(); ++i)
531  ++bit;
532  }
533  score += bscore;
534  }
535 
536  return score / al.size();
537 }
538 
539 static int score_match(const QString &a, const QString &b)
540 {
541  if (a.isEmpty() || b.isEmpty())
542  return 0;
543  if (a == b)
544  return 1000;
545 
546  QString A = a.simplified().toUpper();
547  QString B = b.simplified().toUpper();
548  if (A == B)
549  return 1000;
550 
551 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
552  QStringList al = A.split(" ", QString::SkipEmptyParts);
553 #else
554  QStringList al = A.split(" ", Qt::SkipEmptyParts);
555 #endif
556  if (al.isEmpty())
557  return 0;
558 
559 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
560  QStringList bl = B.split(" ", QString::SkipEmptyParts);
561 #else
562  QStringList bl = B.split(" ", Qt::SkipEmptyParts);
563 #endif
564  if (bl.isEmpty())
565  return 0;
566 
567  // score words symmetrically
568  int score = (score_words(al, bl) + score_words(bl, al)) / 2;
569 
570  return std::min(900, score);
571 }
572 
573 int DBEvent::GetMatch(const std::vector<DBEvent> &programs, int &bestmatch) const
574 {
575  bestmatch = -1;
576  int match_val = INT_MIN;
577  int duration = m_starttime.secsTo(m_endtime);
578 
579  for (size_t i = 0; i < programs.size(); ++i)
580  {
581  int mv = 0;
582  int duration_loop = programs[i].m_starttime.secsTo(programs[i].m_endtime);
583 
584  mv -= qAbs(m_starttime.secsTo(programs[i].m_starttime));
585  mv -= qAbs(m_endtime.secsTo(programs[i].m_endtime));
586  mv -= qAbs(duration - duration_loop);
587  mv += score_match(m_title, programs[i].m_title) * 10;
588  mv += score_match(m_subtitle, programs[i].m_subtitle);
589  mv += score_match(m_description, programs[i].m_description);
590 
591  /* determine overlap of both programs
592  * we don't know which one starts first */
593  int overlap = 0;
594  if (m_starttime < programs[i].m_starttime)
595  overlap = programs[i].m_starttime.secsTo(m_endtime);
596  else if (m_starttime > programs[i].m_starttime)
597  overlap = m_starttime.secsTo(programs[i].m_endtime);
598  else
599  {
600  if (m_endtime <= programs[i].m_endtime)
601  overlap = m_starttime.secsTo(m_endtime);
602  else
603  overlap = m_starttime.secsTo(programs[i].m_endtime);
604  }
605 
606  /* scale the score depending on the overlap length
607  * full score is preserved if the overlap is at least 1/2 of the length
608  * of the shorter program */
609  if (overlap > 0)
610  {
611  /* crappy providers apparently have events without duration
612  * ensure that the minimal duration is 2 second to avoid
613  * multiplying and more importantly dividing by zero */
614  int min_dur = std::max(2, std::min(duration, duration_loop));
615  overlap = std::min(overlap, min_dur/2);
616  mv *= overlap * 2;
617  mv /= min_dur;
618  }
619  else
620  {
621  LOG(VB_GENERAL, LOG_ERR,
622  QString("Unexpected result: shows don't "
623  "overlap\n\t%1: %2 - %3\n\t%4: %5 - %6")
624  .arg(m_title.left(35), 35)
625  .arg(m_starttime.toString(Qt::ISODate),
626  m_endtime.toString(Qt::ISODate))
627  .arg(programs[i].m_title.left(35), 35)
628  .arg(programs[i].m_starttime.toString(Qt::ISODate),
629  programs[i].m_endtime.toString(Qt::ISODate))
630  );
631  }
632 
633  if (mv > match_val)
634  {
635  LOG(VB_EIT, LOG_DEBUG,
636  QString("GM : '%1' new best match '%2' with score %3")
637  .arg(m_title.left(35),
638  programs[i].m_title.left(35),
639  QString::number(mv)));
640  bestmatch = i;
641  match_val = mv;
642  }
643  }
644 
645  return match_val;
646 }
647 
649  MSqlQuery &q, uint chanid, const std::vector<DBEvent> &p, int match) const
650 {
651  // Adjust/delete overlaps;
652  bool ok = true;
653  for (size_t i = 0; i < p.size(); ++i)
654  {
655  if (i != (uint)match)
656  ok &= MoveOutOfTheWayDB(q, chanid, p[i]);
657  }
658 
659  // If we failed to move programs out of the way, don't insert new ones..
660  if (!ok)
661  {
662  LOG(VB_EIT, LOG_DEBUG,
663  QString("EIT: cannot insert '%1' MoveOutOfTheWayDB failed")
664  .arg(m_title.left(35)));
665  return 0;
666  }
667 
668  // No match, insert current item
669  if ((match < 0) || ((uint)match >= p.size()))
670  {
671  LOG(VB_EIT, LOG_DEBUG,
672  QString("EIT: insert '%1'")
673  .arg(m_title.left(35)));
674  return InsertDB(q, chanid);
675  }
676 
677  // Changing a starttime of a program that is being recorded can
678  // start another recording of the same program.
679  // Therefore we skip updates that change a starttime in the past
680  // unless the endtime is later.
681  if (m_starttime != p[match].m_starttime)
682  {
683  QDateTime now = QDateTime::currentDateTimeUtc();
684  if (m_starttime < now && m_endtime <= p[match].m_endtime)
685  {
686  LOG(VB_EIT, LOG_DEBUG,
687  QString("EIT: skip '%1' starttime is in the past")
688  .arg(m_title.left(35)));
689  return 0;
690  }
691  }
692 
693  // Update matched item with current data
694  LOG(VB_EIT, LOG_DEBUG,
695  QString("EIT: update '%1' with '%2'")
696  .arg(p[match].m_title.left(35),
697  m_title.left(35)));
698  return UpdateDB(q, chanid, p[match]);
699 }
700 
701 // Update starttime in table record for single recordings
702 // when the starttime of a program is changed.
703 //
704 // Return the number of rows affected:
705 // 0 if program is not found in table record
706 // 1 if program is found and updated
707 //
708 static int change_record(MSqlQuery &query, uint chanid,
709  const QDateTime &old_starttime,
710  const QDateTime &new_starttime)
711 {
712  query.prepare("UPDATE record "
713  "SET starttime = :NEWSTARTTIME, "
714  " startdate = :NEWSTARTDATE "
715  "WHERE chanid = :CHANID "
716  "AND type = :TYPE "
717  "AND search = :SEARCH "
718  "AND starttime = :OLDSTARTTIME "
719  "AND startdate = :OLDSTARTDATE ");
720  query.bindValue(":CHANID", chanid);
721  query.bindValue(":TYPE", kSingleRecord);
722  query.bindValue(":SEARCH", kNoSearch);
723  query.bindValue(":OLDSTARTTIME", old_starttime.time());
724  query.bindValue(":OLDSTARTDATE", old_starttime.date());
725  query.bindValue(":NEWSTARTTIME", new_starttime.time());
726  query.bindValue(":NEWSTARTDATE", new_starttime.date());
727 
728  int rows = 0;
729  if (!query.exec() || !query.isActive())
730  {
731  MythDB::DBError("Updating record", query);
732  }
733  else
734  {
735  rows = query.numRowsAffected();
736  }
737  if (rows > 0)
738  {
739  LOG(VB_EIT, LOG_DEBUG,
740  QString("EIT: Updated record: chanid:%1 old:%3 new:%4 rows:%5")
741  .arg(QString::number(chanid),
742  old_starttime.toString(Qt::ISODate),
743  new_starttime.toString(Qt::ISODate),
744  QString::number(rows)));
745  }
746  return rows;
747 }
748 
749 // Update matched item with current data.
750 //
752  MSqlQuery &query, uint chanid, const DBEvent &match) const
753 {
754  QString ltitle = m_title;
755  QString lsubtitle = m_subtitle;
756  QString ldesc = m_description;
757  QString lcategory = m_category;
758  uint16_t lairdate = m_airdate;
759  QString lprogramId = m_programId;
760  QString lseriesId = m_seriesId;
761  QString linetref = m_inetref;
762  QDate loriginalairdate = m_originalairdate;
763 
764  // Update starttime also in database table record so that
765  // tables program and record remain consistent.
766  if (m_starttime != match.m_starttime)
767  {
768  QDateTime const &old_starttime = match.m_starttime;
769  QDateTime const &new_starttime = m_starttime;
770  change_record(query, chanid, old_starttime, new_starttime);
771 
772  LOG(VB_EIT, LOG_DEBUG,
773  QString("EIT: (U) change starttime from %1 to %2 for chanid:%3 program '%4' ")
774  .arg(old_starttime.toString(Qt::ISODate),
775  new_starttime.toString(Qt::ISODate),
776  QString::number(chanid),
777  m_title.left(35)));
778  }
779 
780  if (ltitle.isEmpty() && !match.m_title.isEmpty())
781  ltitle = match.m_title;
782 
783  if (lsubtitle.isEmpty() && !match.m_subtitle.isEmpty())
784  lsubtitle = match.m_subtitle;
785 
786  if (ldesc.isEmpty() && !match.m_description.isEmpty())
787  ldesc = match.m_description;
788 
789  if (lcategory.isEmpty() && !match.m_category.isEmpty())
790  lcategory = match.m_category;
791 
792  if (!lairdate && match.m_airdate)
793  lairdate = match.m_airdate;
794 
795  if (!loriginalairdate.isValid() && match.m_originalairdate.isValid())
796  loriginalairdate = match.m_originalairdate;
797 
798  if (lprogramId.isEmpty() && !match.m_programId.isEmpty())
799  lprogramId = match.m_programId;
800 
801  if (lseriesId.isEmpty() && !match.m_seriesId.isEmpty())
802  lseriesId = match.m_seriesId;
803 
804  if (linetref.isEmpty() && !match.m_inetref.isEmpty())
805  linetref= match.m_inetref;
806 
808  if (!m_categoryType && match.m_categoryType)
809  tmp = match.m_categoryType;
810 
811  QString lcattype = myth_category_type_to_string(tmp);
812 
813  unsigned char lsubtype = m_subtitleType | match.m_subtitleType;
814  unsigned char laudio = m_audioProps | match.m_audioProps;
815  unsigned char lvideo = m_videoProps | match.m_videoProps;
816 
817  uint lseason = match.m_season;
818  uint lepisode = match.m_episode;
819  uint lepisodeTotal = match.m_totalepisodes;
820 
822  {
823  lseason = m_season;
824  lepisode = m_episode;
825  lepisodeTotal = m_totalepisodes;
826  }
827 
828  uint lpartnumber = match.m_partnumber;
829  uint lparttotal = match.m_parttotal;
830 
831  if (m_partnumber || m_parttotal)
832  {
833  lpartnumber = m_partnumber;
834  lparttotal = m_parttotal;
835  }
836 
837  bool lpreviouslyshown = m_previouslyshown || match.m_previouslyshown;
838 
839  uint32_t llistingsource = m_listingsource | match.m_listingsource;
840 
841  QString lsyndicatedepisodenumber = m_syndicatedepisodenumber;
842  if (lsyndicatedepisodenumber.isEmpty() &&
843  !match.m_syndicatedepisodenumber.isEmpty())
844  lsyndicatedepisodenumber = match.m_syndicatedepisodenumber;
845 
846  query.prepare(
847  "UPDATE program "
848  "SET title = :TITLE, subtitle = :SUBTITLE, "
849  " description = :DESC, "
850  " category = :CATEGORY, category_type = :CATTYPE, "
851  " starttime = :STARTTIME, endtime = :ENDTIME, "
852  " closecaptioned = :CC, subtitled = :HASSUBTITLES, "
853  " stereo = :STEREO, hdtv = :HDTV, "
854  " subtitletypes = :SUBTYPE, "
855  " audioprop = :AUDIOPROP, videoprop = :VIDEOPROP, "
856  " season = :SEASON, "
857  " episode = :EPISODE, totalepisodes = :TOTALEPS, "
858  " partnumber = :PARTNO, parttotal = :PARTTOTAL, "
859  " syndicatedepisodenumber = :SYNDICATENO, "
860  " airdate = :AIRDATE, originalairdate=:ORIGAIRDATE, "
861  " listingsource = :LSOURCE, "
862  " seriesid = :SERIESID, programid = :PROGRAMID, "
863  " previouslyshown = :PREVSHOWN, inetref = :INETREF "
864  "WHERE chanid = :CHANID AND "
865  " starttime = :OLDSTART ");
866 
867  query.bindValue(":CHANID", chanid);
868  query.bindValue(":OLDSTART", match.m_starttime);
869  query.bindValue(":TITLE", denullify(ltitle));
870  query.bindValue(":SUBTITLE", denullify(lsubtitle));
871  query.bindValue(":DESC", denullify(ldesc));
872  query.bindValue(":CATEGORY", denullify(lcategory));
873  query.bindValue(":CATTYPE", lcattype);
874  query.bindValue(":STARTTIME", m_starttime);
875  query.bindValue(":ENDTIME", m_endtime);
876  query.bindValue(":CC", (lsubtype & SUB_HARDHEAR) != 0);
877  query.bindValue(":HASSUBTITLES",(lsubtype & SUB_NORMAL) != 0);
878  query.bindValue(":STEREO", (laudio & AUD_STEREO) != 0);
879  query.bindValue(":HDTV", (lvideo & VID_HDTV) != 0);
880  query.bindValue(":SUBTYPE", lsubtype);
881  query.bindValue(":AUDIOPROP", laudio);
882  query.bindValue(":VIDEOPROP", lvideo);
883  query.bindValue(":SEASON", lseason);
884  query.bindValue(":EPISODE", lepisode);
885  query.bindValue(":TOTALEPS", lepisodeTotal);
886  query.bindValue(":PARTNO", lpartnumber);
887  query.bindValue(":PARTTOTAL", lparttotal);
888  query.bindValue(":SYNDICATENO", denullify(lsyndicatedepisodenumber));
889  query.bindValue(":AIRDATE", lairdate ? QString::number(lairdate) : "0000");
890  query.bindValue(":ORIGAIRDATE", loriginalairdate);
891  query.bindValue(":LSOURCE", llistingsource);
892  query.bindValue(":SERIESID", denullify(lseriesId));
893  query.bindValue(":PROGRAMID", denullify(lprogramId));
894  query.bindValue(":PREVSHOWN", lpreviouslyshown);
895  query.bindValue(":INETREF", linetref);
896 
897  if (!query.exec())
898  {
899  MythDB::DBError("UpdateDB", query);
900  return 0;
901  }
902 
903  if (m_credits)
904  {
905  for (auto & credit : *m_credits)
906  credit.InsertDB(query, chanid, m_starttime);
907  }
908 
909  for (const auto & rating : qAsConst(m_ratings))
910  {
911  query.prepare(
912  "INSERT IGNORE INTO programrating "
913  " ( chanid, starttime, `system`, rating) "
914  "VALUES (:CHANID, :START, :SYS, :RATING)");
915  query.bindValue(":CHANID", chanid);
916  query.bindValue(":START", m_starttime);
917  query.bindValue(":SYS", rating.m_system);
918  query.bindValue(":RATING", rating.m_rating);
919 
920  if (!query.exec())
921  MythDB::DBError("programrating insert", query);
922  }
923 
924  add_genres(query, m_genres, chanid, m_starttime);
925 
926  return 1;
927 }
928 
929 static bool delete_program(MSqlQuery &query, uint chanid, const QDateTime &st)
930 {
931  query.prepare(
932  "DELETE from program "
933  "WHERE chanid = :CHANID AND "
934  " starttime = :STARTTIME");
935 
936  query.bindValue(":CHANID", chanid);
937  query.bindValue(":STARTTIME", st);
938 
939  if (!query.exec())
940  {
941  MythDB::DBError("delete_program", query);
942  return false;
943  }
944 
945  query.prepare(
946  "DELETE from credits "
947  "WHERE chanid = :CHANID AND "
948  " starttime = :STARTTIME");
949 
950  query.bindValue(":CHANID", chanid);
951  query.bindValue(":STARTTIME", st);
952 
953  if (!query.exec())
954  {
955  MythDB::DBError("delete_credits", query);
956  return false;
957  }
958 
959  query.prepare(
960  "DELETE from programrating "
961  "WHERE chanid = :CHANID AND "
962  " starttime = :STARTTIME");
963 
964  query.bindValue(":CHANID", chanid);
965  query.bindValue(":STARTTIME", st);
966 
967  if (!query.exec())
968  {
969  MythDB::DBError("delete_rating", query);
970  return false;
971  }
972 
973  query.prepare(
974  "DELETE from programgenres "
975  "WHERE chanid = :CHANID AND "
976  " starttime = :STARTTIME");
977 
978  query.bindValue(":CHANID", chanid);
979  query.bindValue(":STARTTIME", st);
980 
981  if (!query.exec())
982  {
983  MythDB::DBError("delete_genres", query);
984  return false;
985  }
986 
987  return true;
988 }
989 
990 static bool program_exists(MSqlQuery &query, uint chanid, const QDateTime &st)
991 {
992  query.prepare(
993  "SELECT title FROM program "
994  "WHERE chanid = :CHANID AND "
995  " starttime = :OLDSTART");
996  query.bindValue(":CHANID", chanid);
997  query.bindValue(":OLDSTART", st);
998  if (!query.exec())
999  {
1000  MythDB::DBError("program_exists", query);
1001  }
1002  return query.next();
1003 }
1004 
1005 static bool change_program(MSqlQuery &query, uint chanid, const QDateTime &st,
1006  const QDateTime &new_st, const QDateTime &new_end)
1007 {
1008  query.prepare(
1009  "UPDATE program "
1010  "SET starttime = :NEWSTART, "
1011  " endtime = :NEWEND "
1012  "WHERE chanid = :CHANID AND "
1013  " starttime = :OLDSTART");
1014 
1015  query.bindValue(":CHANID", chanid);
1016  query.bindValue(":OLDSTART", st);
1017  query.bindValue(":NEWSTART", new_st);
1018  query.bindValue(":NEWEND", new_end);
1019 
1020  if (!query.exec())
1021  {
1022  MythDB::DBError("change_program", query);
1023  return false;
1024  }
1025 
1026  query.prepare(
1027  "UPDATE credits "
1028  "SET starttime = :NEWSTART "
1029  "WHERE chanid = :CHANID AND "
1030  " starttime = :OLDSTART");
1031 
1032  query.bindValue(":CHANID", chanid);
1033  query.bindValue(":OLDSTART", st);
1034  query.bindValue(":NEWSTART", new_st);
1035 
1036  if (!query.exec())
1037  {
1038  MythDB::DBError("change_credits", query);
1039  return false;
1040  }
1041 
1042  query.prepare(
1043  "UPDATE programrating "
1044  "SET starttime = :NEWSTART "
1045  "WHERE chanid = :CHANID AND "
1046  " starttime = :OLDSTART");
1047 
1048  query.bindValue(":CHANID", chanid);
1049  query.bindValue(":OLDSTART", st);
1050  query.bindValue(":NEWSTART", new_st);
1051 
1052  if (!query.exec())
1053  {
1054  MythDB::DBError("change_rating", query);
1055  return false;
1056  }
1057 
1058  query.prepare(
1059  "UPDATE programgenres "
1060  "SET starttime = :NEWSTART "
1061  "WHERE chanid = :CHANID AND "
1062  " starttime = :OLDSTART");
1063 
1064  query.bindValue(":CHANID", chanid);
1065  query.bindValue(":OLDSTART", st);
1066  query.bindValue(":NEWSTART", new_st);
1067 
1068  if (!query.exec())
1069  {
1070  MythDB::DBError("change_genres", query);
1071  return false;
1072  }
1073 
1074  return true;
1075 }
1076 
1077 // Move the program "prog" (3rd parameter) out of the way
1078 // because it overlaps with our new program.
1080  MSqlQuery &query, uint chanid, const DBEvent &prog) const
1081 {
1082  if (prog.m_starttime >= m_starttime && prog.m_endtime <= m_endtime)
1083  {
1084  // Old program completely inside our new program.
1085  // Delete the old program completely.
1086  LOG(VB_EIT, LOG_DEBUG,
1087  QString("EIT: delete '%1' %2 - %3")
1088  .arg(prog.m_title.left(35),
1089  prog.m_starttime.toString(Qt::ISODate),
1090  prog.m_endtime.toString(Qt::ISODate)));
1091  return delete_program(query, chanid, prog.m_starttime);
1092  }
1093  if (prog.m_starttime < m_starttime && prog.m_endtime > m_starttime)
1094  {
1095  // Old program starts before, but ends during or after our new program.
1096  // Adjust the end time of the old program to the start time
1097  // of our new program.
1098  // This will leave a hole after our new program when the end time of
1099  // the old program was after the end time of the new program!!
1100  LOG(VB_EIT, LOG_DEBUG,
1101  QString("EIT: change '%1' endtime to %2")
1102  .arg(prog.m_title.left(35),
1103  m_starttime.toString(Qt::ISODate)));
1104  return change_program(query, chanid, prog.m_starttime,
1105  prog.m_starttime, // Keep the start time
1106  m_starttime); // New end time is our start time
1107  }
1108  if (prog.m_starttime < m_endtime && prog.m_endtime > m_endtime)
1109  {
1110  // Old program starts during, but ends after our new program.
1111  // Adjust the starttime of the old program to the end time
1112  // of our new program.
1113  // If there is already a program starting just when our
1114  // new program ends we cannot move the old program
1115  // so then we have to delete the old program.
1116  if (program_exists(query, chanid, m_endtime))
1117  {
1118  LOG(VB_EIT, LOG_DEBUG,
1119  QString("EIT: delete '%1' %2 - %3")
1120  .arg(prog.m_title.left(35),
1121  prog.m_starttime.toString(Qt::ISODate),
1122  prog.m_endtime.toString(Qt::ISODate)));
1123  return delete_program(query, chanid, prog.m_starttime);
1124  }
1125  LOG(VB_EIT, LOG_DEBUG,
1126  QString("EIT: (M) change starttime from %1 to %2 for chanid:%3 program '%4' ")
1127  .arg(prog.m_starttime.toString(Qt::ISODate),
1128  m_endtime.toString(Qt::ISODate),
1129  QString::number(chanid),
1130  prog.m_title.left(35)));
1131 
1132  // Update starttime in tables record and program so they stay consistent.
1133  change_record(query, chanid, prog.m_starttime, m_endtime);
1134  return change_program(query, chanid, prog.m_starttime,
1135  m_endtime, // New start time is our endtime
1136  prog.m_endtime); // Keep the end time
1137  }
1138  // must be non-conflicting...
1139  return true;
1140 }
1141 
1146  bool recording) const
1147 {
1148  QString table = recording ? "recordedprogram" : "program";
1149 
1150  query.prepare(QString(
1151  "REPLACE INTO %1 ("
1152  " chanid, title, subtitle, description, "
1153  " category, category_type, "
1154  " starttime, endtime, "
1155  " closecaptioned, stereo, hdtv, subtitled, "
1156  " subtitletypes, audioprop, videoprop, "
1157  " stars, partnumber, parttotal, "
1158  " syndicatedepisodenumber, "
1159  " airdate, originalairdate,listingsource, "
1160  " seriesid, programid, previouslyshown, "
1161  " season, episode, totalepisodes, "
1162  " inetref ) "
1163  "VALUES ("
1164  " :CHANID, :TITLE, :SUBTITLE, :DESCRIPTION, "
1165  " :CATEGORY, :CATTYPE, "
1166  " :STARTTIME, :ENDTIME, "
1167  " :CC, :STEREO, :HDTV, :HASSUBTITLES, "
1168  " :SUBTYPES, :AUDIOPROP, :VIDEOPROP, "
1169  " :STARS, :PARTNUMBER, :PARTTOTAL, "
1170  " :SYNDICATENO, "
1171  " :AIRDATE, :ORIGAIRDATE, :LSOURCE, "
1172  " :SERIESID, :PROGRAMID, :PREVSHOWN, "
1173  " :SEASON, :EPISODE, :TOTALEPISODES, "
1174  " :INETREF ) ").arg(table));
1175 
1176  QString cattype = myth_category_type_to_string(m_categoryType);
1177  query.bindValue(":CHANID", chanid);
1178  query.bindValue(":TITLE", denullify(m_title));
1179  query.bindValue(":SUBTITLE", denullify(m_subtitle));
1180  query.bindValue(":DESCRIPTION", denullify(m_description));
1181  query.bindValue(":CATEGORY", denullify(m_category));
1182  query.bindValue(":CATTYPE", cattype);
1183  query.bindValue(":STARTTIME", m_starttime);
1184  query.bindValue(":ENDTIME", m_endtime);
1185  query.bindValue(":CC", (m_subtitleType & SUB_HARDHEAR) != 0);
1186  query.bindValue(":STEREO", (m_audioProps & AUD_STEREO) != 0);
1187  query.bindValue(":HDTV", (m_videoProps & VID_HDTV) != 0);
1188  query.bindValue(":HASSUBTITLES",(m_subtitleType & SUB_NORMAL) != 0);
1189  query.bindValue(":SUBTYPES", m_subtitleType);
1190  query.bindValue(":AUDIOPROP", m_audioProps);
1191  query.bindValue(":VIDEOPROP", m_videoProps);
1192  query.bindValue(":STARS", m_stars);
1193  query.bindValue(":PARTNUMBER", m_partnumber);
1194  query.bindValue(":PARTTOTAL", m_parttotal);
1195  query.bindValue(":SYNDICATENO", denullify(m_syndicatedepisodenumber));
1196  query.bindValue(":AIRDATE", m_airdate ? QString::number(m_airdate) : "0000");
1197  query.bindValue(":ORIGAIRDATE", m_originalairdate);
1198  query.bindValue(":LSOURCE", m_listingsource);
1199  query.bindValue(":SERIESID", denullify(m_seriesId));
1200  query.bindValue(":PROGRAMID", denullify(m_programId));
1201  query.bindValue(":PREVSHOWN", m_previouslyshown);
1202  query.bindValue(":SEASON", m_season);
1203  query.bindValue(":EPISODE", m_episode);
1204  query.bindValue(":TOTALEPISODES", m_totalepisodes);
1205  query.bindValue(":INETREF", m_inetref);
1206 
1207  if (!query.exec())
1208  {
1209  MythDB::DBError("InsertDB", query);
1210  return 0;
1211  }
1212 
1213  table = recording ? "recordedrating" : "programrating";
1214  for (const auto & rating : qAsConst(m_ratings))
1215  {
1216  query.prepare(QString(
1217  "INSERT IGNORE INTO %1 "
1218  " ( chanid, starttime, `system`, rating) "
1219  "VALUES (:CHANID, :START, :SYS, :RATING)").arg(table));
1220  query.bindValue(":CHANID", chanid);
1221  query.bindValue(":START", m_starttime);
1222  query.bindValue(":SYS", rating.m_system);
1223  query.bindValue(":RATING", rating.m_rating);
1224 
1225  if (!query.exec())
1226  MythDB::DBError("programrating insert", query);
1227  }
1228 
1229  if (m_credits)
1230  {
1231  for (auto & credit : *m_credits)
1232  credit.InsertDB(query, chanid, m_starttime, recording);
1233  }
1234 
1235  add_genres(query, m_genres, chanid, m_starttime);
1236 
1237  return 1;
1238 }
1239 
1241  DBEvent(other.m_listingsource)
1242 {
1243  *this = other;
1244 }
1245 
1247 {
1248  if (this == &other)
1249  return *this;
1250 
1251  DBEvent::operator=(other);
1252 
1253  m_channel = other.m_channel;
1254  m_startts = other.m_startts;
1255  m_endts = other.m_endts;
1257  m_showtype = other.m_showtype;
1258  m_colorcode = other.m_colorcode;
1259  m_clumpidx = other.m_clumpidx;
1260  m_clumpmax = other.m_clumpmax;
1261 
1262  m_channel.squeeze();
1263  m_startts.squeeze();
1264  m_endts.squeeze();
1265  m_title_pronounce.squeeze();
1266  m_showtype.squeeze();
1267  m_colorcode.squeeze();
1268  m_clumpidx.squeeze();
1269  m_clumpmax.squeeze();
1270 
1271  return *this;
1272 }
1273 
1275 {
1276  DBEvent::Squeeze();
1277  m_channel.squeeze();
1278  m_startts.squeeze();
1279  m_endts.squeeze();
1280  m_title_pronounce.squeeze();
1281  m_showtype.squeeze();
1282  m_colorcode.squeeze();
1283  m_clumpidx.squeeze();
1284  m_clumpmax.squeeze();
1285 }
1286 
1301  bool recording) const
1302 {
1303  QString table = recording ? "recordedprogram" : "program";
1304 
1305  LOG(VB_XMLTV, LOG_DEBUG,
1306  QString("Inserting new %1 : %2 - %3 %4 %5")
1307  .arg(table,
1308  m_starttime.toString(Qt::ISODate),
1309  m_endtime.toString(Qt::ISODate),
1310  m_channel));
1311 
1312  query.prepare(QString(
1313  "REPLACE INTO %1 ("
1314  " chanid, title, subtitle, description, "
1315  " category, category_type, "
1316  " starttime, endtime, "
1317  " closecaptioned, stereo, hdtv, subtitled, "
1318  " subtitletypes, audioprop, videoprop, "
1319  " partnumber, parttotal, "
1320  " syndicatedepisodenumber, "
1321  " airdate, originalairdate,listingsource, "
1322  " seriesid, programid, previouslyshown, "
1323  " stars, showtype, title_pronounce, colorcode, "
1324  " season, episode, totalepisodes, "
1325  " inetref ) "
1326  "VALUES("
1327  " :CHANID, :TITLE, :SUBTITLE, :DESCRIPTION, "
1328  " :CATEGORY, :CATTYPE, "
1329  " :STARTTIME, :ENDTIME, "
1330  " :CC, :STEREO, :HDTV, :HASSUBTITLES, "
1331  " :SUBTYPES, :AUDIOPROP, :VIDEOPROP, "
1332  " :PARTNUMBER, :PARTTOTAL, "
1333  " :SYNDICATENO, "
1334  " :AIRDATE, :ORIGAIRDATE, :LSOURCE, "
1335  " :SERIESID, :PROGRAMID, :PREVSHOWN, "
1336  " :STARS, :SHOWTYPE, :TITLEPRON, :COLORCODE, "
1337  " :SEASON, :EPISODE, :TOTALEPISODES, "
1338  " :INETREF )").arg(table));
1339 
1340  QString cattype = myth_category_type_to_string(m_categoryType);
1341 
1342  query.bindValue(":CHANID", chanid);
1343  query.bindValue(":TITLE", denullify(m_title));
1344  query.bindValue(":SUBTITLE", denullify(m_subtitle));
1345  query.bindValue(":DESCRIPTION", denullify(m_description));
1346  query.bindValue(":CATEGORY", denullify(m_category));
1347  query.bindValue(":CATTYPE", cattype);
1348  query.bindValue(":STARTTIME", m_starttime);
1349  query.bindValue(":ENDTIME", denullify(m_endtime));
1350  query.bindValue(":CC",
1351  (m_subtitleType & SUB_HARDHEAR) != 0);
1352  query.bindValue(":STEREO",
1353  (m_audioProps & AUD_STEREO) != 0);
1354  query.bindValue(":HDTV",
1355  (m_videoProps & VID_HDTV) != 0);
1356  query.bindValue(":HASSUBTITLES",
1357  (m_subtitleType & SUB_NORMAL) != 0);
1358  query.bindValue(":SUBTYPES", m_subtitleType);
1359  query.bindValue(":AUDIOPROP", m_audioProps);
1360  query.bindValue(":VIDEOPROP", m_videoProps);
1361  query.bindValue(":PARTNUMBER", m_partnumber);
1362  query.bindValue(":PARTTOTAL", m_parttotal);
1363  query.bindValue(":SYNDICATENO", denullify(m_syndicatedepisodenumber));
1364  query.bindValue(":AIRDATE", m_airdate ? QString::number(m_airdate):"0000");
1365  query.bindValue(":ORIGAIRDATE", m_originalairdate);
1366  query.bindValue(":LSOURCE", m_listingsource);
1367  query.bindValue(":SERIESID", denullify(m_seriesId));
1368  query.bindValue(":PROGRAMID", denullify(m_programId));
1369  query.bindValue(":PREVSHOWN", m_previouslyshown);
1370  query.bindValue(":STARS", m_stars);
1371  query.bindValue(":SHOWTYPE", m_showtype);
1372  query.bindValue(":TITLEPRON", m_title_pronounce);
1373  query.bindValue(":COLORCODE", m_colorcode);
1374  query.bindValue(":SEASON", m_season);
1375  query.bindValue(":EPISODE", m_episode);
1376  query.bindValue(":TOTALEPISODES", m_totalepisodes);
1377  query.bindValue(":INETREF", m_inetref);
1378 
1379  if (!query.exec())
1380  {
1381  MythDB::DBError(table + " insert", query);
1382  return 0;
1383  }
1384 
1385  table = recording ? "recordedrating" : "programrating";
1386  for (const auto & rating : m_ratings)
1387  {
1388  query.prepare(QString("INSERT IGNORE INTO %1 "
1389  " ( chanid, starttime, `system`, rating) "
1390  "VALUES (:CHANID, :START, :SYS, :RATING)")
1391  .arg(table));
1392  query.bindValue(":CHANID", chanid);
1393  query.bindValue(":START", m_starttime);
1394  query.bindValue(":SYS", rating.m_system);
1395  query.bindValue(":RATING", rating.m_rating);
1396 
1397  if (!query.exec())
1398  MythDB::DBError(QString("%1 insert").arg(table), query);
1399  }
1400 
1401  if (m_credits)
1402  {
1403  for (auto & credit : *m_credits)
1404  credit.InsertDB(query, chanid, m_starttime, recording);
1405  }
1406 
1407  add_genres(query, m_genres, chanid, m_starttime);
1408 
1409  return 1;
1410 }
1411 
1413  uint chanid, const QDateTime &from, const QDateTime &to,
1414  bool use_channel_time_offset)
1415 {
1416  std::chrono::seconds secs = 0s;
1417  if (use_channel_time_offset)
1418  secs = ChannelUtil::GetTimeOffset(chanid);
1419 
1420  QDateTime newFrom = from.addSecs(secs.count());
1421  QDateTime newTo = to.addSecs(secs.count());
1422 
1423  MSqlQuery query(MSqlQuery::InitCon());
1424  query.prepare("DELETE FROM program "
1425  "WHERE starttime >= :FROM AND starttime < :TO "
1426  "AND chanid = :CHANID ;");
1427  query.bindValue(":FROM", newFrom);
1428  query.bindValue(":TO", newTo);
1429  query.bindValue(":CHANID", chanid);
1430  bool ok = query.exec();
1431 
1432  query.prepare("DELETE FROM programrating "
1433  "WHERE starttime >= :FROM AND starttime < :TO "
1434  "AND chanid = :CHANID ;");
1435  query.bindValue(":FROM", newFrom);
1436  query.bindValue(":TO", newTo);
1437  query.bindValue(":CHANID", chanid);
1438  ok &= query.exec();
1439 
1440  query.prepare("DELETE FROM credits "
1441  "WHERE starttime >= :FROM AND starttime < :TO "
1442  "AND chanid = :CHANID ;");
1443  query.bindValue(":FROM", newFrom);
1444  query.bindValue(":TO", newTo);
1445  query.bindValue(":CHANID", chanid);
1446  ok &= query.exec();
1447 
1448  query.prepare("DELETE FROM programgenres "
1449  "WHERE starttime >= :FROM AND starttime < :TO "
1450  "AND chanid = :CHANID ;");
1451  query.bindValue(":FROM", newFrom);
1452  query.bindValue(":TO", newTo);
1453  query.bindValue(":CHANID", chanid);
1454  ok &= query.exec();
1455 
1456  return ok;
1457 }
1458 
1460  uint sourceid, const QDateTime &from, const QDateTime &to,
1461  bool use_channel_time_offset)
1462 {
1463  std::vector<uint> chanids = ChannelUtil::GetChanIDs(sourceid);
1464 
1465  bool ok = true;
1466  auto cleardata = [&](uint chanid)
1467  { ok &= ClearDataByChannel(chanid, from, to, use_channel_time_offset); };
1468  std::for_each(chanids.cbegin(), chanids.cend(), cleardata);
1469  return ok;
1470 }
1471 
1472 static bool start_time_less_than(const DBEvent *a, const DBEvent *b)
1473 {
1474  return (a->m_starttime < b->m_starttime);
1475 }
1476 
1477 void ProgramData::FixProgramList(QList<ProgInfo*> &fixlist)
1478 {
1479  std::stable_sort(fixlist.begin(), fixlist.end(), start_time_less_than);
1480 
1481  QList<ProgInfo*>::iterator it = fixlist.begin();
1482  while (true)
1483  {
1484  QList<ProgInfo*>::iterator cur = it;
1485  ++it;
1486 
1487  // fill in miss stop times
1488  if ((*cur)->m_endts.isEmpty() || (*cur)->m_startts > (*cur)->m_endts)
1489  {
1490  if (it != fixlist.end())
1491  {
1492  (*cur)->m_endts = (*it)->m_startts;
1493  (*cur)->m_endtime = (*it)->m_starttime;
1494  }
1495  /* if its the last programme in the file then leave its
1496  endtime as 0000-00-00 00:00:00 so we can find it easily in
1497  fix_end_times() */
1498  }
1499 
1500  if (it == fixlist.end())
1501  break;
1502 
1503  // remove overlapping programs
1504  if ((*cur)->HasTimeConflict(**it))
1505  {
1506  QList<ProgInfo*>::iterator tokeep;
1507  QList<ProgInfo*>::iterator todelete;
1508 
1509  if ((*cur)->m_endtime <= (*cur)->m_starttime)
1510  tokeep = it, todelete = cur; // NOLINT(bugprone-branch-clone)
1511  else if ((*it)->m_endtime <= (*it)->m_starttime)
1512  tokeep = cur, todelete = it; // NOLINT(bugprone-branch-clone)
1513  else if (!(*cur)->m_subtitle.isEmpty() &&
1514  (*it)->m_subtitle.isEmpty())
1515  tokeep = cur, todelete = it;
1516  else if (!(*it)->m_subtitle.isEmpty() &&
1517  (*cur)->m_subtitle.isEmpty())
1518  tokeep = it, todelete = cur;
1519  else if (!(*cur)->m_description.isEmpty() &&
1520  (*it)->m_description.isEmpty())
1521  tokeep = cur, todelete = it;
1522  else
1523  tokeep = it, todelete = cur;
1524 
1525 
1526  LOG(VB_XMLTV, LOG_DEBUG,
1527  QString("Removing conflicting program: %1 - %2 %3 %4")
1528  .arg((*todelete)->m_starttime.toString(Qt::ISODate),
1529  (*todelete)->m_endtime.toString(Qt::ISODate),
1530  (*todelete)->m_channel,
1531  (*todelete)->m_title));
1532 
1533  LOG(VB_XMLTV, LOG_DEBUG,
1534  QString("Conflicted with : %1 - %2 %3 %4")
1535  .arg((*tokeep)->m_starttime.toString(Qt::ISODate),
1536  (*tokeep)->m_endtime.toString(Qt::ISODate),
1537  (*tokeep)->m_channel,
1538  (*tokeep)->m_title));
1539 
1540  bool step_back = todelete == it;
1541  it = fixlist.erase(todelete);
1542  if (step_back)
1543  --it;
1544  }
1545  }
1546 }
1547 
1557  uint sourceid, QMap<QString, QList<ProgInfo> > &proglist)
1558 {
1559  uint unchanged = 0;
1560  uint updated = 0;
1561 
1562  MSqlQuery query(MSqlQuery::InitCon());
1563 
1564  QMap<QString, QList<ProgInfo> >::const_iterator mapiter;
1565  for (mapiter = proglist.cbegin(); mapiter != proglist.cend(); ++mapiter)
1566  {
1567  if (mapiter.key().isEmpty())
1568  continue;
1569 
1570  query.prepare(
1571  "SELECT chanid "
1572  "FROM channel "
1573  "WHERE deleted IS NULL AND "
1574  " sourceid = :ID AND "
1575  " xmltvid = :XMLTVID");
1576  query.bindValue(":ID", sourceid);
1577  query.bindValue(":XMLTVID", mapiter.key());
1578 
1579  if (!query.exec())
1580  {
1581  MythDB::DBError("ProgramData::HandlePrograms", query);
1582  continue;
1583  }
1584 
1585  std::vector<uint> chanids;
1586  while (query.next())
1587  chanids.push_back(query.value(0).toUInt());
1588 
1589  if (chanids.empty())
1590  {
1591  LOG(VB_GENERAL, LOG_NOTICE,
1592  QString("Unknown xmltv channel identifier: %1"
1593  " - Skipping channel.").arg(mapiter.key()));
1594  continue;
1595  }
1596 
1597  QList<ProgInfo> &list = proglist[mapiter.key()];
1598  QList<ProgInfo*> sortlist;
1599  // NOLINTNEXTLINE(modernize-loop-convert)
1600  for (auto it = list.begin(); it != list.end(); ++it)
1601  sortlist.push_back(&(*it));
1602 
1603  FixProgramList(sortlist);
1604 
1605  for (uint chanid : chanids)
1606  HandlePrograms(query, chanid, sortlist, unchanged, updated);
1607  }
1608 
1609  LOG(VB_GENERAL, LOG_INFO,
1610  QString("Updated programs: %1 Unchanged programs: %2")
1611  .arg(updated) .arg(unchanged));
1612 }
1613 
1626  uint chanid,
1627  const QList<ProgInfo*> &sortlist,
1628  uint &unchanged,
1629  uint &updated)
1630 {
1631  for (auto *pinfo : qAsConst(sortlist))
1632  {
1633  if (IsUnchanged(query, chanid, *pinfo))
1634  {
1635  unchanged++;
1636  continue;
1637  }
1638 
1639  if (!DeleteOverlaps(query, chanid, *pinfo))
1640  continue;
1641 
1642  updated += pinfo->InsertDB(query, chanid);
1643  }
1644 }
1645 
1647 {
1648  int count = 0;
1649  QString chanid;
1650  QString starttime;
1651  QString endtime;
1652  QString querystr;
1653  MSqlQuery query1(MSqlQuery::InitCon());
1654  MSqlQuery query2(MSqlQuery::InitCon());
1655 
1656  querystr = "SELECT chanid, starttime, endtime FROM program "
1657  "WHERE endtime = '0000-00-00 00:00:00' "
1658  "ORDER BY chanid, starttime;";
1659 
1660  if (!query1.exec(querystr))
1661  {
1662  LOG(VB_GENERAL, LOG_ERR,
1663  QString("fix_end_times query failed: %1").arg(querystr));
1664  return -1;
1665  }
1666 
1667  while (query1.next())
1668  {
1669  starttime = query1.value(1).toString();
1670  chanid = query1.value(0).toString();
1671  endtime = query1.value(2).toString();
1672 
1673  querystr = QString("SELECT chanid, starttime, endtime FROM program "
1674  "WHERE starttime > '%1' "
1675  "AND chanid = '%2' "
1676  "ORDER BY starttime LIMIT 1;")
1677  .arg(starttime, chanid);
1678 
1679  if (!query2.exec(querystr))
1680  {
1681  LOG(VB_GENERAL, LOG_ERR,
1682  QString("fix_end_times query failed: %1").arg(querystr));
1683  return -1;
1684  }
1685 
1686  if (query2.next() && (endtime != query2.value(1).toString()))
1687  {
1688  count++;
1689  endtime = query2.value(1).toString();
1690  querystr = QString("UPDATE program SET "
1691  "endtime = '%2' WHERE (chanid = '%3' AND "
1692  "starttime = '%4');")
1693  .arg(endtime, chanid, starttime);
1694 
1695  if (!query2.exec(querystr))
1696  {
1697  LOG(VB_GENERAL, LOG_ERR,
1698  QString("fix_end_times query failed: %1").arg(querystr));
1699  return -1;
1700  }
1701  }
1702  }
1703 
1704  return count;
1705 }
1706 
1708  MSqlQuery &query, uint chanid, const ProgInfo &pi)
1709 {
1710  query.prepare(
1711  "SELECT count(*) "
1712  "FROM program "
1713  "WHERE chanid = :CHANID AND "
1714  " starttime = :START AND "
1715  " endtime = :END AND "
1716  " title = :TITLE AND "
1717  " subtitle = :SUBTITLE AND "
1718  " description = :DESC AND "
1719  " category = :CATEGORY AND "
1720  " category_type = :CATEGORY_TYPE AND "
1721  " airdate = :AIRDATE AND "
1722  " stars >= (:STARS1 - 0.001) AND "
1723  " stars <= (:STARS2 + 0.001) AND "
1724  " previouslyshown = :PREVIOUSLYSHOWN AND "
1725  " title_pronounce = :TITLE_PRONOUNCE AND "
1726  " audioprop = :AUDIOPROP AND "
1727  " videoprop = :VIDEOPROP AND "
1728  " subtitletypes = :SUBTYPES AND "
1729  " partnumber = :PARTNUMBER AND "
1730  " parttotal = :PARTTOTAL AND "
1731  " seriesid = :SERIESID AND "
1732  " showtype = :SHOWTYPE AND "
1733  " colorcode = :COLORCODE AND "
1734  " syndicatedepisodenumber = :SYNDICATEDEPISODENUMBER AND "
1735  " programid = :PROGRAMID AND "
1736  " season = :SEASON AND "
1737  " episode = :EPISODE AND "
1738  " totalepisodes = :TOTALEPISODES AND "
1739  " inetref = :INETREF");
1740 
1741  QString cattype = myth_category_type_to_string(pi.m_categoryType);
1742 
1743  query.bindValue(":CHANID", chanid);
1744  query.bindValue(":START", pi.m_starttime);
1745  query.bindValue(":END", pi.m_endtime);
1746  query.bindValue(":TITLE", denullify(pi.m_title));
1747  query.bindValue(":SUBTITLE", denullify(pi.m_subtitle));
1748  query.bindValue(":DESC", denullify(pi.m_description));
1749  query.bindValue(":CATEGORY", denullify(pi.m_category));
1750  query.bindValue(":CATEGORY_TYPE", cattype);
1751  query.bindValue(":AIRDATE", pi.m_airdate);
1752  query.bindValue(":STARS1", pi.m_stars);
1753  query.bindValue(":STARS2", pi.m_stars);
1754  query.bindValue(":PREVIOUSLYSHOWN", pi.m_previouslyshown);
1755  query.bindValue(":TITLE_PRONOUNCE", pi.m_title_pronounce);
1756  query.bindValue(":AUDIOPROP", pi.m_audioProps);
1757  query.bindValue(":VIDEOPROP", pi.m_videoProps);
1758  query.bindValue(":SUBTYPES", pi.m_subtitleType);
1759  query.bindValue(":PARTNUMBER", pi.m_partnumber);
1760  query.bindValue(":PARTTOTAL", pi.m_parttotal);
1761  query.bindValue(":SERIESID", denullify(pi.m_seriesId));
1762  query.bindValue(":SHOWTYPE", pi.m_showtype);
1763  query.bindValue(":COLORCODE", pi.m_colorcode);
1764  query.bindValue(":SYNDICATEDEPISODENUMBER",
1766  query.bindValue(":PROGRAMID", denullify(pi.m_programId));
1767  query.bindValue(":SEASON", pi.m_season);
1768  query.bindValue(":EPISODE", pi.m_episode);
1769  query.bindValue(":TOTALEPISODES", pi.m_totalepisodes);
1770  query.bindValue(":INETREF", pi.m_inetref);
1771 
1772  if (query.exec() && query.next())
1773  return query.value(0).toUInt() > 0;
1774 
1775  return false;
1776 }
1777 
1779  MSqlQuery &query, uint chanid, const ProgInfo &pi)
1780 {
1781  if (VERBOSE_LEVEL_CHECK(VB_XMLTV, LOG_DEBUG))
1782  {
1783  // Get overlaps..
1784  query.prepare(
1785  "SELECT title,starttime,endtime "
1786  "FROM program "
1787  "WHERE chanid = :CHANID AND "
1788  " starttime >= :START AND "
1789  " starttime < :END;");
1790  query.bindValue(":CHANID", chanid);
1791  query.bindValue(":START", pi.m_starttime);
1792  query.bindValue(":END", pi.m_endtime);
1793 
1794  if (!query.exec())
1795  return false;
1796 
1797  if (!query.next())
1798  return true;
1799 
1800  do
1801  {
1802  LOG(VB_XMLTV, LOG_DEBUG,
1803  QString("Removing existing program: %1 - %2 %3 %4")
1804  .arg(MythDate::as_utc(query.value(1).toDateTime()).toString(Qt::ISODate),
1805  MythDate::as_utc(query.value(2).toDateTime()).toString(Qt::ISODate),
1806  pi.m_channel,
1807  query.value(0).toString()));
1808  } while (query.next());
1809  }
1810 
1811  if (!ClearDataByChannel(chanid, pi.m_starttime, pi.m_endtime, false))
1812  {
1813  LOG(VB_XMLTV, LOG_ERR,
1814  QString("Program delete failed : %1 - %2 %3 %4")
1815  .arg(pi.m_starttime.toString(Qt::ISODate),
1816  pi.m_endtime.toString(Qt::ISODate),
1817  pi.m_channel,
1818  pi.m_title));
1819  return false;
1820  }
1821 
1822  return true;
1823 }
DBEvent::m_season
uint m_season
Definition: programdata.h:172
DBPerson::InsertCreditsDB
uint InsertCreditsDB(MSqlQuery &query, uint personid, uint roleid, uint chanid, const QDateTime &starttime, bool recording=false) const
Definition: programdata.cpp:204
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:216
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:807
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
ProgInfo::m_clumpmax
QString m_clumpmax
Definition: programdata.h:250
ProgInfo::m_showtype
QString m_showtype
Definition: programdata.h:247
DBPerson::toString
QString toString(void) const
Definition: programdata.cpp:121
DBEvent::m_totalepisodes
uint m_totalepisodes
Definition: programdata.h:174
mythdb.h
DBEvent::m_videoProps
unsigned char m_videoProps
Definition: programdata.h:162
DBEvent::GetOverlappingPrograms
uint GetOverlappingPrograms(MSqlQuery &query, uint chanid, std::vector< DBEvent > &programs) const
Definition: programdata.cpp:429
score_words
static int score_words(const QStringList &al, const QStringList &bl)
Definition: programdata.cpp:506
MythDate::as_utc
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:27
DBEvent::m_previouslyshown
bool m_previouslyshown
Definition: programdata.h:168
DBEvent::InsertDB
virtual uint InsertDB(MSqlQuery &query, uint chanid, bool recording=false) const
Insert Callback function when Allow Re-record is pressed in Watch Recordings.
Definition: programdata.cpp:1145
DBPerson
Definition: programdata.h:24
score_match
static int score_match(const QString &a, const QString &b)
Definition: programdata.cpp:539
programdata.h
DBEvent::m_starttime
QDateTime m_starttime
Definition: programdata.h:152
start_time_less_than
static bool start_time_less_than(const DBEvent *a, const DBEvent *b)
Definition: programdata.cpp:1472
VERBOSE_LEVEL_CHECK
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:608
DBEvent::m_audioProps
unsigned char m_audioProps
Definition: programdata.h:161
ProgInfo::ProgInfo
ProgInfo()
Definition: programdata.h:229
DBPerson::m_name
QString m_name
Definition: programdata.h:69
DBEvent::AddPerson
void AddPerson(DBPerson::Role role, const QString &name, int priority=0, const QString &character="")
Definition: programdata.cpp:298
DBEvent::m_partnumber
uint16_t m_partnumber
Definition: programdata.h:157
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
DBPerson::kGuest
@ kGuest
Definition: programdata.h:41
DBPerson::operator=
DBPerson & operator=(const DBPerson &rhs)
Definition: programdata.cpp:70
DBPerson::m_priority
int m_priority
Definition: programdata.h:70
change_record
static int change_record(MSqlQuery &query, uint chanid, const QDateTime &old_starttime, const QDateTime &new_starttime)
Definition: programdata.cpp:708
ProgInfo::operator=
ProgInfo & operator=(const ProgInfo &other)
Definition: programdata.cpp:1246
DBEvent::m_credits
DBCredits * m_credits
Definition: programdata.h:156
DBEvent::m_category
QString m_category
Definition: programdata.h:151
DBEvent::m_listingsource
uint32_t m_listingsource
Definition: programdata.h:169
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
DBCredits
std::vector< DBPerson > DBCredits
Definition: programdata.h:73
DBEvent::m_seriesId
QString m_seriesId
Definition: programdata.h:165
ProgInfo::Squeeze
void Squeeze(void) override
Definition: programdata.cpp:1274
DBEvent::operator=
DBEvent & operator=(const DBEvent &other)
Definition: programdata.cpp:231
string_to_myth_category_type
ProgramInfo::CategoryType string_to_myth_category_type(const QString &category_type)
Definition: programinfo.cpp:137
DBEvent::m_parttotal
uint16_t m_parttotal
Definition: programdata.h:158
DBEvent::m_stars
float m_stars
Definition: programdata.h:163
denullify
static QString denullify(const QString &str)
Definition: programdata.cpp:29
change_program
static bool change_program(MSqlQuery &query, uint chanid, const QDateTime &st, const QDateTime &new_st, const QDateTime &new_end)
Definition: programdata.cpp:1005
ProgInfo::m_endts
QString m_endts
Definition: programdata.h:245
DBEvent::m_programId
QString m_programId
Definition: programdata.h:166
ProgramData::ClearDataBySource
static bool ClearDataBySource(uint sourceid, const QDateTime &from, const QDateTime &to, bool use_channel_time_offset)
Definition: programdata.cpp:1459
mythlogging.h
DBEvent::m_categoryType
ProgramInfo::CategoryType m_categoryType
Definition: programdata.h:164
ProgramData::fix_end_times
static int fix_end_times(void)
Definition: programdata.cpp:1646
program_exists
static bool program_exists(MSqlQuery &query, uint chanid, const QDateTime &st)
Definition: programdata.cpp:990
DBEvent
Definition: programdata.h:82
DBEvent::m_title
QString m_title
Definition: programdata.h:148
hardwareprofile.config.p
p
Definition: config.py:33
DBEvent::GetMatch
int GetMatch(const std::vector< DBEvent > &programs, int &bestmatch) const
Definition: programdata.cpp:573
ProgInfo::m_colorcode
QString m_colorcode
Definition: programdata.h:248
DBPerson::InsertPersonDB
uint InsertPersonDB(MSqlQuery &query) const
Definition: programdata.cpp:162
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:540
DBEvent::m_subtitle
QString m_subtitle
Definition: programdata.h:149
DBEvent::m_syndicatedepisodenumber
QString m_syndicatedepisodenumber
Definition: programdata.h:159
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:227
DBEvent::UpdateDB
uint UpdateDB(MSqlQuery &query, uint chanid, int match_threshold) const
Definition: programdata.cpp:325
hardwareprofile.scan.rating
def rating(profile, smoonURL, gate)
Definition: scan.py:39
ProgramData::IsUnchanged
static bool IsUnchanged(MSqlQuery &query, uint chanid, const ProgInfo &pi)
Definition: programdata.cpp:1707
roles
static const std::array< const std::string, DBPerson::kGuest+1 > roles
Definition: programdata.cpp:22
ProgInfo::m_channel
QString m_channel
Definition: programdata.h:243
ProgramData::DeleteOverlaps
static bool DeleteOverlaps(MSqlQuery &query, uint chanid, const ProgInfo &pi)
Definition: programdata.cpp:1778
ProgramData::FixProgramList
static void FixProgramList(QList< ProgInfo * > &fixlist)
Definition: programdata.cpp:1477
add_genres
static void add_genres(MSqlQuery &query, const QStringList &genres, uint chanid, const QDateTime &starttime)
Definition: programdata.cpp:39
DBPerson::DBPerson
DBPerson(const DBPerson &other)
Definition: programdata.cpp:60
kNoSearch
@ kNoSearch
Definition: recordingtypes.h:80
uint
unsigned int uint
Definition: compat.h:79
ProgramInfo::CategoryType
CategoryType
Definition: programinfo.h:76
DBEvent::Squeeze
virtual void Squeeze(void)
Definition: programdata.cpp:286
DBPerson::InsertRoleDB
bool InsertRoleDB(MSqlQuery &query) const
Definition: programdata.cpp:190
DBEvent::m_episode
uint m_episode
Definition: programdata.h:173
channelutil.h
myth_category_type_to_string
QString myth_category_type_to_string(ProgramInfo::CategoryType category_type)
Definition: programinfo.cpp:128
ProgramData::ClearDataByChannel
static bool ClearDataByChannel(uint chanid, const QDateTime &from, const QDateTime &to, bool use_channel_time_offset)
Definition: programdata.cpp:1412
ChannelUtil::GetTimeOffset
static std::chrono::minutes GetTimeOffset(int chan_id)
Returns the listings time offset in minutes for given channel.
Definition: channelutil.cpp:780
ProgInfo::InsertDB
uint InsertDB(MSqlQuery &query, uint chanid, bool recording=false) const override
Insert a single entry into the "program" database.
Definition: programdata.cpp:1300
DBEvent::m_ratings
QList< EventRating > m_ratings
Definition: programdata.h:170
DBPerson::GetRole
QString GetRole(void) const
Definition: programdata.cpp:114
delete_program
static bool delete_program(MSqlQuery &query, uint chanid, const QDateTime &st)
Definition: programdata.cpp:929
DBEvent::m_description
QString m_description
Definition: programdata.h:150
DBEvent::HasTimeConflict
bool HasTimeConflict(const DBEvent &other) const
Definition: programdata.cpp:318
DBPerson::Role
Role
Definition: programdata.h:28
DBPerson::m_role
Role m_role
Definition: programdata.h:68
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:883
std
Definition: mythchrono.h:23
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:17
ProgInfo
Definition: programdata.h:226
kSingleRecord
@ kSingleRecord
Definition: recordingtypes.h:23
DBEvent::MoveOutOfTheWayDB
bool MoveOutOfTheWayDB(MSqlQuery &query, uint chanid, const DBEvent &prog) const
Definition: programdata.cpp:1079
DBEvent::m_subtitleType
unsigned char m_subtitleType
Definition: programdata.h:160
DBEvent::m_endtime
QDateTime m_endtime
Definition: programdata.h:153
DBPerson::GetRoleDB
uint GetRoleDB(MSqlQuery &query) const
Definition: programdata.cpp:176
DBPerson::InsertDB
uint InsertDB(MSqlQuery &query, uint chanid, const QDateTime &starttime, bool recording=false) const
Definition: programdata.cpp:126
MSqlQuery::numRowsAffected
int numRowsAffected() const
Definition: mythdbcon.h:218
DBPerson::m_character
QString m_character
Definition: programdata.h:71
uint16_t
unsigned short uint16_t
Definition: iso6937tables.h:3
ProgramData::HandlePrograms
static void HandlePrograms(uint sourceid, QMap< QString, QList< ProgInfo > > &proglist)
Called from mythfilldatabase to bulk insert data into the program database.
Definition: programdata.cpp:1556
DBEvent::m_inetref
QString m_inetref
Definition: programdata.h:167
DBEvent::m_genres
QStringList m_genres
Definition: programdata.h:171
DBEvent::m_airdate
uint16_t m_airdate
movie year / production year
Definition: programdata.h:154
ChannelUtil::GetChanIDs
static std::vector< uint > GetChanIDs(int sourceid=-1, bool onlyVisible=false)
Definition: channelutil.cpp:2155
ProgInfo::m_startts
QString m_startts
Definition: programdata.h:244
kUnknown
@ kUnknown
Unprocessable file type.
Definition: imagetypes.h:35
ProgInfo::m_clumpidx
QString m_clumpidx
Definition: programdata.h:249
DBEvent::m_originalairdate
QDate m_originalairdate
origial broadcast date
Definition: programdata.h:155
ProgInfo::m_title_pronounce
QString m_title_pronounce
Definition: programdata.h:246
DBPerson::GetPersonDB
uint GetPersonDB(MSqlQuery &query) const
Definition: programdata.cpp:146
DBPerson::kActor
@ kActor
Definition: programdata.h:31
dvbdescriptors.h
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:832