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